From f711d1d5001f86854fae8f5a80870f8508320276 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Wed, 15 Apr 2026 17:43:53 -0600 Subject: [PATCH 01/40] Centralize EOCV-Sim's lifecycles into a central scope & rewrite InputSourceManager to kotlin --- .gitignore | 3 +- .../eocvsim/util/event/EventListener.kt | 1 - .../main/java/dalvik/system/VMRuntime.java | 2 +- .../github/deltacv/eocvsim/plugin/api/Api.kt | 2 +- .../api/exception/EOCVSimApiException.kt | 2 +- .../eocvsim/virtualreflect/VirtualField.kt | 3 + .../virtualreflect/VirtualReflectContext.kt | 20 + .../virtualreflect/VirtualReflection.kt | 3 + .../eocvsim/virtualreflect/jvm/Label.java | 3 + .../github/serivesmejia/eocvsim/EOCVSim.kt | 41 +- .../serivesmejia/eocvsim/gui/Visualizer.java | 18 +- .../tuner/TunableFieldPanelConfig.kt | 2 +- .../tuner/element/TunableTextField.java | 4 +- .../gui/component/visualizer/TopMenuBar.kt | 4 +- .../pipeline/PipelineSelectorButtonsPanel.kt | 2 +- .../pipeline/SourceSelectorPanel.kt | 6 +- .../serivesmejia/eocvsim/gui/dialog/Output.kt | 4 +- .../eocvsim/gui/dialog/PluginOutput.kt | 2 +- .../gui/dialog/source/CreateCameraSource.kt | 2 +- .../gui/dialog/source/CreateHttpSource.kt | 2 +- .../gui/dialog/source/CreateImageSource.kt | 2 +- .../gui/dialog/source/CreateVideoSource.kt | 2 +- .../eocvsim/input/InputSourceInitializer.kt | 8 +- .../eocvsim/input/InputSourceManager.java | 356 ------------------ .../eocvsim/input/InputSourceManager.kt | 321 ++++++++++++++++ .../eocvsim/pipeline/PipelineManager.kt | 7 +- .../compiler/CompiledPipelineManager.kt | 31 +- .../eocvsim/plugin/api/impl/EOCVSimApiImpl.kt | 2 +- .../eocvsim/util/ClasspathScan.kt | 4 +- .../eocvsim/workspace/WorkspaceManager.kt | 4 +- .../eocvsim/workspace/util/VSCodeLauncher.kt | 4 +- .../external/gui/SwingOpenCvViewport.kt | 44 +-- 32 files changed, 462 insertions(+), 449 deletions(-) delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt diff --git a/.gitignore b/.gitignore index 3928031c..604e21ee 100644 --- a/.gitignore +++ b/.gitignore @@ -113,4 +113,5 @@ fabric.properties *.DS_Store -imgui.ini \ No newline at end of file +imgui.ini +.vscode \ No newline at end of file diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventListener.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventListener.kt index 6ee2196b..3477752f 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventListener.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventListener.kt @@ -39,7 +39,6 @@ class EventListenerContext( private val handler: EventHandler, private val id: EventListenerId, ) { - /** * Removes the listener from the event handler */ diff --git a/Common/src/main/java/dalvik/system/VMRuntime.java b/Common/src/main/java/dalvik/system/VMRuntime.java index 78c1e13a..b3102c97 100644 --- a/Common/src/main/java/dalvik/system/VMRuntime.java +++ b/Common/src/main/java/dalvik/system/VMRuntime.java @@ -16,7 +16,7 @@ public static VMRuntime getRuntime() { } public Object newUnpaddedArray(Class componentType, int length) { - return Array.newInstance(componentType, length); // we do a little bit of trolling -SEM + return Array.newInstance(componentType, length); // we do a little bit of trolling } } diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/Api.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/Api.kt index 9be0d843..2cb43493 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/Api.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/Api.kt @@ -51,7 +51,7 @@ abstract class Api(val owner: EOCVSimPlugin) { /** * Simple name of the owning plugin class, for error messages */ - val ownerName: String get() = owner::class.java.simpleName + val ownerName: String get() = owner::class.simpleName ?: "UnknownPlugin" /** * Whether this API has been disabled. diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/exception/EOCVSimApiException.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/exception/EOCVSimApiException.kt index 243f0243..d9c97cd8 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/exception/EOCVSimApiException.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/exception/EOCVSimApiException.kt @@ -25,4 +25,4 @@ package io.github.deltacv.eocvsim.plugin.api.exception import io.github.deltacv.eocvsim.plugin.api.Api -class EOCVSimApiException(message: String, api: Api) : RuntimeException(message) \ No newline at end of file +class EOCVSimApiException(message: String, val api: Api) : RuntimeException("Exception thrown by API ${api::class.simpleName} $message") \ No newline at end of file diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualField.kt b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualField.kt index 71efc62c..b3124ab7 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualField.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualField.kt @@ -23,6 +23,9 @@ package io.github.deltacv.eocvsim.virtualreflect +/** + * Represents a field of a class, but is not necessarily backed by an actual Java Field. + */ interface VirtualField { val name: String diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflectContext.kt b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflectContext.kt index 7583c027..059db68c 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflectContext.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflectContext.kt @@ -23,15 +23,35 @@ package io.github.deltacv.eocvsim.virtualreflect +/** + * Context for virtual reflection, which makes for a multi-platform way to reflect on classes, fields, and methods. + */ interface VirtualReflectContext { + /** + * The name of the class this context is reflecting on, including package name + */ val name: String + + /** + * The simple name of the class this context is reflecting on, without package name + */ val simpleName: String + /** + * The fields of the class this context is reflecting on + */ val fields: Array + /** + * Gets a field by its name, or null if it doesn't exist + */ fun getField(name: String): VirtualField? + /** + * Gets a field by its label, or null if it doesn't exist + * @see io.github.deltacv.eocvsim.virtualreflect.jvm.Label + */ fun getLabeledField(label: String): VirtualField? } \ No newline at end of file diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflection.kt b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflection.kt index 27a68b9e..f4f414bf 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflection.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflection.kt @@ -23,6 +23,9 @@ package io.github.deltacv.eocvsim.virtualreflect +/** + * Interface for virtual reflection, which allows to get a [VirtualReflectContext] from a class or an instance + */ interface VirtualReflection { fun contextOf(c: Class<*>): VirtualReflectContext? diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/Label.java b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/Label.java index 18069124..0b050c6b 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/Label.java +++ b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/Label.java @@ -28,6 +28,9 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Annotation to label fields in a class. This is used to identify fields that are used in the virtual reflection system. + */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Label { 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 19f9e29f..78d5e191 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 @@ -48,12 +48,17 @@ 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 kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel import io.github.deltacv.eocvsim.plugin.loader.PluginManager -import io.github.deltacv.vision.external.PipelineRenderHook +import org.openftc.easyopencv.OpenCvViewport import nu.pattern.OpenCV import org.opencv.core.Mat import org.opencv.core.Size import org.openftc.easyopencv.TimestampedPipelineHandler +import android.graphics.Canvas import java.awt.Dimension import java.io.File import java.lang.Thread.sleep @@ -169,6 +174,12 @@ class EOCVSim(val params: Parameters = Parameters()) { */ @JvmField val inputSourceManager = InputSourceManager(this) + /** + * Manager in charge of loading and managing plugins + * @see PluginManager + */ + @JvmField val pluginManager = PluginManager(this) + /** * The pipeline statistics calculator instance in charge of * calculating the average FPS, pipeline time and overhead time @@ -223,15 +234,11 @@ class EOCVSim(val params: Parameters = Parameters()) { */ val fpsLimiter = FpsLimiter(30.0) - /** - * Manager in charge of loading and managing plugins - * @see PluginManager - */ - val pluginManager = PluginManager(this) - lateinit var eocvSimThread: Thread private set + @JvmField val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) + private val hexCode = Integer.toHexString(hashCode()) private var isRestarting = false @@ -247,6 +254,14 @@ class EOCVSim(val params: Parameters = Parameters()) { USER_REQUESTED, THREAD_EXIT, RESTART, CRASH } + private val pipelineRenderHook = + OpenCvViewport.RenderHook { + canvas, onscreenWidth, onscreenHeight, scaleBmpPxToCanvasPx, scaleCanvasDensity, userContext -> + if (pipelineManager.hasInitCurrentPipeline) { + pipelineManager.currentPipeline?.onDrawFrame(canvas, onscreenWidth, onscreenHeight, scaleBmpPxToCanvasPx, scaleCanvasDensity, userContext) + } + } + /** * Initializes the simulator * This method is called to initialize all the components @@ -284,7 +299,7 @@ class EOCVSim(val params: Parameters = Parameters()) { loadOpenCvLib(params.opencvNativeLibrary) if (!hasScanned) { - classpathScan.asyncScan() + classpathScan.asyncScan(scope) hasScanned = true } @@ -370,7 +385,7 @@ class EOCVSim(val params: Parameters = Parameters()) { if(pipelineManager.currentPipeline !is OpMode && pipelineManager.currentPipeline != null) { visualizer.viewport.activate() - visualizer.viewport.setRenderHook(PipelineRenderHook) // calls OpenCvPipeline#onDrawFrame on the viewport (UI) thread + visualizer.viewport.setRenderHook(pipelineRenderHook) // calls OpenCvPipeline#onDrawFrame on the viewport (UI) thread } else { // opmodes are on their own, lol visualizer.viewport.deactivate() @@ -446,11 +461,8 @@ class EOCVSim(val params: Parameters = Parameters()) { inputSourceManager.update(pipelineManager.paused) tunerManager.update() - pipelineManager.update( - if (inputSourceManager.lastMatFromSource != null && !inputSourceManager.lastMatFromSource.empty()) { - inputSourceManager.lastMatFromSource - } else null - ) + val lastMat = inputSourceManager.lastMatFromSource + pipelineManager.update(lastMat) //limit FPS fpsLimiter.maxFPS = config.pipelineMaxFps.fps.toDouble() @@ -485,6 +497,7 @@ class EOCVSim(val params: Parameters = Parameters()) { visualizer.close() destroying = true + scope.cancel() if(reason == DestroyReason.THREAD_EXIT) { exitProcess(0) 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 99af9304..500b205a 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 @@ -24,6 +24,7 @@ package com.github.serivesmejia.eocvsim.gui; import com.formdev.flatlaf.FlatLaf; +import kotlinx.coroutines.CoroutineScope; import com.github.serivesmejia.eocvsim.Build; import com.github.serivesmejia.eocvsim.EOCVSim; import com.github.serivesmejia.eocvsim.gui.component.CollapsiblePanelX; @@ -329,21 +330,6 @@ public void updateTunerFields(List fields) { tunerCollapsible.setVisible(!fields.isEmpty()); } - public void asyncCompilePipelines() { - if (PipelineCompiler.Companion.getIS_USABLE()) { - menuBar.workspCompile.setEnabled(false); - pipelineSelectorPanel.getButtonsPanel().getPipelineCompileBtt().setEnabled(false); - - eocvSim.pipelineManager.compiledPipelineManager.asyncBuild((result) -> { - menuBar.workspCompile.setEnabled(true); - pipelineSelectorPanel.getButtonsPanel().getPipelineCompileBtt().setEnabled(true); - - return Unit.INSTANCE; - }); - } else { - compilerUnsupported(); - } - } public void compilerUnsupported() { asyncPleaseWaitDialog( @@ -402,7 +388,7 @@ public void askOpenVSCode() { "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()); + VSCodeLauncher.INSTANCE.asyncLaunch(eocvSim.workspaceManager.getWorkspaceFile(), eocvSim.scope); } } ); diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelConfig.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelConfig.kt index 4c1cc071..52968d0d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelConfig.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelConfig.kt @@ -159,7 +159,7 @@ class TunableFieldPanelConfig(private val fieldOptions: TunableFieldPanelOptions it.closeOnFocusLost = true //launch the waiting in the background - GlobalScope.launch { + eocvSim.scope.launch { delay(100) //close config popup if still hasn't focused after a bit launch(Dispatchers.Swing) { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.java index 5b5f9703..4d7013f8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.java @@ -123,7 +123,9 @@ public void replace(FilterBypass fb, int offset, int length, String text, Attrib Runnable changeFieldValue = () -> { if(tunableField.shouldIgnoreGuiUpdates()) return; - if ((!hasValidText || !tunableField.isOnlyNumbers() || !getText().trim().equals(""))) { + String text = getText(); + + if ((!hasValidText || !tunableField.isOnlyNumbers() || (text != null && !getText().trim().equals("")))) { try { tunableField.setFieldValueFromGui(index, getText()); } catch (Exception e) { 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 b49e8d5b..829d25c7 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 @@ -137,7 +137,7 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { mWorkspMenu.addSeparator() - workspCompile.addActionListener { visualizer.asyncCompilePipelines() } + workspCompile.addActionListener { eocvSim.pipelineManager.compiledPipelineManager.asyncBuild() } mWorkspMenu.add(workspCompile) val workspBuildOutput = JMenuItem("Output") @@ -162,7 +162,7 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { val workspVSCodeOpen = JMenuItem("Open VS Code Here") workspVSCodeOpen.addActionListener { - VSCodeLauncher.asyncLaunch(eocvSim.workspaceManager.workspaceFile) + VSCodeLauncher.asyncLaunch(eocvSim.workspaceManager.workspaceFile, eocvSim.scope) } workspVSCode.add(workspVSCodeOpen) 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 cf5d9bbf..d0ca7c1d 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 @@ -114,7 +114,7 @@ class PipelineSelectorButtonsPanel(eocvSim: EOCVSim) : JPanel(GridBagLayout()) { gridy = 0 }) - pipelineCompileBtt.addActionListener { eocvSim.visualizer.asyncCompilePipelines() } + pipelineCompileBtt.addActionListener { eocvSim.pipelineManager.compiledPipelineManager.asyncBuild() } workspaceButtonsPanel.add(pipelineCompileBtt, GridBagConstraints().apply { gridx = 2 gridy = 0 diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt index 4f5536db..f4f9b0bd 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt @@ -159,13 +159,17 @@ class SourceSelectorPanel(private val eocvSim: EOCVSim) : JPanel() { sourceSelector.selectedIndex = (index - 1).clipUpperZero() } } + + eocvSim.inputSourceManager.onSourcesListChange { + updateSourcesList() + } } fun updateSourcesList(): Job { SwingUtilities.invokeLater { val listModel = DefaultListModel() - for (source in eocvSim.inputSourceManager.sortedInputSources) { + eocvSim.inputSourceManager.sortedInputSources.forEach { source -> listModel.addElement(source.name) } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Output.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Output.kt index b25bc677..b6ebb307 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Output.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Output.kt @@ -102,7 +102,7 @@ class Output @JvmOverloads constructor( } @OptIn(DelicateCoroutinesApi::class) - private fun registerListeners() = GlobalScope.launch(Dispatchers.Swing) { + private fun registerListeners() = eocvSim.scope.launch(Dispatchers.Swing) { output.addWindowListener(object: WindowAdapter() { override fun windowClosing(e: WindowEvent) { close() @@ -165,7 +165,7 @@ class Output @JvmOverloads constructor( } buildBottomButtonsPanel.buildAgainButton.addActionListener { - eocvSim.visualizer.asyncCompilePipelines() + eocvSim.pipelineManager.compiledPipelineManager.asyncBuild() } } 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 570e3d1b..eca2dec0 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 @@ -111,7 +111,7 @@ class PluginOutput( } @OptIn(DelicateCoroutinesApi::class) - private fun registerListeners() = GlobalScope.launch(Dispatchers.Swing) { + private fun registerListeners() = (eocvSim?.scope ?: GlobalScope).launch(Dispatchers.Swing) { output.addWindowListener(object : java.awt.event.WindowAdapter() { override fun windowClosing(e: java.awt.event.WindowEvent?) { if(mavenBottomButtonsPanel.continueButton.isEnabled) { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt index ee72d18c..ac9b4e5f 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt @@ -333,7 +333,7 @@ class CreateCameraSource( private fun updateCreateButton() { createButton.isEnabled = nameTextField.text.trim().isNotEmpty() && - !eocvSim.inputSourceManager.isNameOnUse(nameTextField.text) + !eocvSim.inputSourceManager.isNameInUse(nameTextField.text) } private fun getSelectedIndex() = indexes[camerasComboBox.selectedItem] ?: 0 diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateHttpSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateHttpSource.kt index d38a1fd1..eda30e10 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateHttpSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateHttpSource.kt @@ -122,7 +122,7 @@ class CreateHttpSource(parent: JFrame, private val eocvSim: EOCVSim) { private fun updateCreateButton() { val isNameValid = nameTextField.text.isNotBlank() && - !eocvSim.inputSourceManager.isNameOnUse(nameTextField.text) + !eocvSim.inputSourceManager.isNameInUse(nameTextField.text) val isUrlValid = urlField.text.isNotBlank() createButton.isEnabled = isNameValid && isUrlValid } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt index 4a553522..97613e24 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt @@ -176,6 +176,6 @@ class CreateImageSource( nameTextField.text.trim().isNotEmpty() && sizeFieldsInput.valid && selectedValidImage && - !eocvSim.inputSourceManager.isNameOnUse(nameTextField.text) + !eocvSim.inputSourceManager.isNameInUse(nameTextField.text) } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt index 292882e5..54441464 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt @@ -159,7 +159,7 @@ class CreateVideoSource( private fun updateCreateButton() { val isNameValid = nameTextField.text.isNotBlank() && - !eocvsim.inputSourceManager.isNameOnUse(nameTextField.text) + !eocvsim.inputSourceManager.isNameInUse(nameTextField.text) createButton.isEnabled = isNameValid && sizeFields.valid && selectedValidVideo } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt index 6e5af4c8..95ac637f 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt @@ -18,7 +18,9 @@ object InputSourceInitializer { fun initializeWithTimeout(inputSource: InputSource, manager: InputSourceManager? = null): Boolean { var result = false - val job = GlobalScope.launch { + val scope = manager?.eocvSim?.scope ?: GlobalScope + + val job = scope.launch { try { result = inputSource.init() } catch (e: Exception) { @@ -50,7 +52,9 @@ object InputSourceInitializer { fun runWithTimeout(sourceName: String, manager: InputSourceManager? = null, callback: () -> Boolean): Boolean { var result = false - val job = GlobalScope.launch { + val scope = manager?.eocvSim?.scope ?: GlobalScope + + val job = scope.launch { try { result = callback() } catch (e: Exception) { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.java deleted file mode 100644 index e9889b4b..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.java +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.input; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.gui.Visualizer; -import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SourceSelectorPanel; -import com.github.serivesmejia.eocvsim.input.source.ImageSource; -import com.github.serivesmejia.eocvsim.input.source.NullSource; -import com.github.serivesmejia.eocvsim.pipeline.PipelineManager; -import com.github.serivesmejia.eocvsim.util.SysUtil; -import kotlinx.coroutines.Job; -import org.opencv.core.Mat; -import org.opencv.core.Scalar; -import org.opencv.core.Size; -import org.opencv.imgproc.Imgproc; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.*; -import java.awt.*; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.util.*; -import java.util.concurrent.CancellationException; - -public class InputSourceManager { - - private static final Scalar BLACK = new Scalar(0, 0, 0, 255); - - private final EOCVSim eocvSim; - - public volatile Mat lastMatFromSource = null; - public volatile InputSource currentInputSource = null; - - public volatile HashMap sources = new HashMap<>(); - - public InputSourceLoader inputSourceLoader = new InputSourceLoader(); - public SourceSelectorPanel selectorPanel; - - private String defaultSource = ""; - - Logger logger = LoggerFactory.getLogger(getClass()); - - public InputSourceManager(EOCVSim eocvSim) { - this.eocvSim = eocvSim; - selectorPanel = eocvSim.visualizer.sourceSelectorPanel; - } - - public void init() { - logger.info("Initializing..."); - - if(lastMatFromSource == null) - lastMatFromSource = new Mat(); - - Size size = new Size(640, 480); - - createDefaultImgInputSource("/images/ug_4.jpg", "ug_eocvsim_4.jpg", "Ultimate Goal 4 Ring", size); - createDefaultImgInputSource("/images/ug_1.jpg", "ug_eocvsim_1.jpg", "Ultimate Goal 1 Ring", size); - createDefaultImgInputSource("/images/ug_0.jpg", "ug_eocvsim_0.jpg", "Ultimate Goal 0 Ring", size); - - if(sources.isEmpty()) { - logger.warn("No input sources found, creating default null source"); - - NullSource nullSource = new NullSource(); - nullSource.isDefault = true; - - addInputSource("Default", nullSource); - } else { - setInputSource("Ultimate Goal 4 Ring", true); - } - - inputSourceLoader.loadInputSourcesFromFile(); - - for (Map.Entry entry : inputSourceLoader.loadedInputSources.entrySet()) { - logger.info("Loaded input source " + entry.getKey()); - addInputSource(entry.getKey(), entry.getValue()); - } - } - - private void createDefaultImgInputSource(String resourcePath, String fileName, String sourceName, Size imgSize) { - try { - InputStream is = InputSource.class.getResourceAsStream(resourcePath); - File f = SysUtil.copyFileIsTemp(is, fileName, true).file; - - ImageSource src = new ImageSource(f.getAbsolutePath(), imgSize); - src.isDefault = true; - src.createdOn = sources.size(); - - addInputSource(sourceName, src); - } catch (IOException e) { - logger.error("Error while creating default image input source", e); - } - } - - public void update(boolean isPaused) { - if(currentInputSource == null) return; - - try { - currentInputSource.setPaused(isPaused); - - Mat m = currentInputSource.update(); - - if(m != null && !m.empty()) { - lastMatFromSource.setTo(BLACK); // clear previous mat - m.copyTo(lastMatFromSource); - // add an extra alpha channel because that's what eocv returns for some reason... (more realistic simulation lol) - Imgproc.cvtColor(lastMatFromSource, lastMatFromSource, Imgproc.COLOR_RGB2RGBA); - } - } catch(Exception ex) { - logger.error("Error while processing current source", ex); - logger.warn("Changing to default source"); - - setInputSource(defaultSource); - } - } - - - public void addInputSource(String name, InputSource inputSource) { - addInputSource(name, inputSource, false); - } - - public void addInputSource(String name, InputSource inputSource, boolean dispatchedByUser) { - if (inputSource == null) { - return; - } - - if (sources.containsKey(name)) return; - - inputSource.eocvSim = eocvSim; - - if(eocvSim.visualizer.sourceSelectorPanel != null) { - eocvSim.visualizer.sourceSelectorPanel.setAllowSourceSwitching(false); - } - inputSource.name = name; - - sources.put(name, inputSource); - - if(inputSource.createdOn == -1) - inputSource.createdOn = System.currentTimeMillis(); - - if(!inputSource.isDefault) { - inputSourceLoader.saveInputSource(name, inputSource); - inputSourceLoader.saveInputSourcesToFile(); - } - - if(eocvSim.visualizer.sourceSelectorPanel != null) { - SourceSelectorPanel selectorPanel = eocvSim.visualizer.sourceSelectorPanel; - - selectorPanel.updateSourcesList(); - - SwingUtilities.invokeLater(() -> { - JList sourceSelector = selectorPanel.getSourceSelector(); - - int currentSourceIndex = sourceSelector.getSelectedIndex(); - - if(dispatchedByUser) { - int index = selectorPanel.getIndexOf(name); - - sourceSelector.setSelectedIndex(index); - - requestSetInputSource(name); - - eocvSim.onMainUpdate.once(() -> { - eocvSim.pipelineManager.requestSetPaused(false); - pauseIfImageTwoFrames(); - }); - } else { - sourceSelector.setSelectedIndex(currentSourceIndex); - } - - selectorPanel.setAllowSourceSwitching(true); - }); - } - - logger.info("Adding InputSource " + inputSource + " (" + inputSource.getClass().getSimpleName() + ")"); - } - - public void deleteInputSource(String sourceName) { - InputSource src = sources.get(sourceName); - - if (src == null) return; - if (src.isDefault) return; - - sources.remove(sourceName); - - inputSourceLoader.deleteInputSource(sourceName); - inputSourceLoader.saveInputSourcesToFile(); - } - - public boolean setInputSource(String sourceName, boolean makeDefault) { - boolean result = setInputSource(sourceName); - - if(result && makeDefault) { - defaultSource = sourceName; - } - - return result; - } - - public boolean setInputSource(String sourceName) { - InputSource src = null; - - SysUtil.debugLogCalled("setInputSource"); - - if(sourceName == null) { - src = new NullSource(); - } else { - src = sources.get(sourceName); - } - - if (src != null) { - src.reset(); - } - - if (src != null) { - if (!InputSourceInitializer.INSTANCE.initializeWithTimeout(src, this)) { - eocvSim.visualizer.asyncPleaseWaitDialog("Error while loading requested source", "Falling back to previous source", - "Close", new Dimension(300, 150), true, true); - - logger.error("Error while loading requested source (" + sourceName + ") reported by itself (init method returned false)"); - - return false; - } - } - - if (currentInputSource != null) { - currentInputSource.reset(); - } - - currentInputSource = src; - - //if pause on images option is turned on by user - if (eocvSim.configManager.getConfig().pauseOnImages) - pauseIfImage(); - - logger.info("Set InputSource to " + currentInputSource.toString() + " (" + src.getClass().getSimpleName() + ")"); - - return true; - } - - public void cleanSourceIfDirty() { - if(currentInputSource != null) { - currentInputSource.cleanIfDirty(); - } - } - - public boolean isNameOnUse(String name) { - return sources.containsKey(name); - } - - public String tryName(String name) { - String sourceName = name; - int count = 0; - - while(eocvSim.inputSourceManager.isNameOnUse(sourceName)) { - count++; - sourceName = name + " (" + count + ")"; - } - - return sourceName; - } - - public void pauseIfImage() { - if(currentInputSource == null) return; - - //if the new input source is an image, we will pause the next frame - //to execute one shot analysis on images and save resources. - if (SourceType.fromClass(currentInputSource.getClass()) == SourceType.IMAGE) { - eocvSim.onMainUpdate.once(() -> - eocvSim.pipelineManager.setPaused( - true, - PipelineManager.PauseReason.IMAGE_ONE_ANALYSIS - ) - ); - } - } - - public void pauseIfImageTwoFrames() { - //if the new input source is an image, we will pause the next frame - //to execute one shot analysis on images and save resources. - eocvSim.onMainUpdate.once(this::pauseIfImage); - } - - public void requestSetInputSource(String name) { - SysUtil.debugLogCalled("requestSetInputSource"); - - eocvSim.onMainUpdate.once(() -> setInputSource(name)); - } - - public Visualizer.AsyncPleaseWaitDialog showApwdIfNeeded(String sourceName, Job job) { - Visualizer.AsyncPleaseWaitDialog apwd = null; - - if (getSourceType(sourceName) == SourceType.CAMERA || getSourceType(sourceName) == SourceType.VIDEO || getSourceType(sourceName) == SourceType.HTTP) { - apwd = eocvSim.visualizer.asyncPleaseWaitDialog( - "Opening source...", null, "Cancel", - new Dimension(300, 150), true - ); - - apwd.onCancel(() -> { - if (job != null) { - job.cancel(new CancellationException()); - } - }); - } - - return apwd; - } - - public String getDefaultInputSource() { - return defaultSource; - } - - public SourceType getSourceType(String sourceName) { - if(sourceName == null) { - return SourceType.UNKNOWN; - } - - InputSource source = sources.get(sourceName); - - if(source == null) { - return SourceType.UNKNOWN; - } - return SourceType.fromClass(source.getClass()); - } - - public InputSource[] getSortedInputSources() { - ArrayList sources = new ArrayList<>(this.sources.values()); - Collections.sort(sources); - - return sources.toArray(new InputSource[0]); - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt new file mode 100644 index 00000000..7bc8c85b --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.input + +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.gui.Visualizer +import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SourceSelectorPanel +import com.github.serivesmejia.eocvsim.input.source.ImageSource +import com.github.serivesmejia.eocvsim.input.source.NullSource +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import com.github.serivesmejia.eocvsim.util.SysUtil +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import io.github.deltacv.common.util.loggerForThis +import kotlinx.coroutines.Job +import org.opencv.core.Mat +import org.opencv.core.Scalar +import org.opencv.core.Size +import org.opencv.imgproc.Imgproc +import java.awt.Dimension +import java.io.File +import java.io.IOException +import java.util.concurrent.CancellationException +import javax.swing.SwingUtilities + +class InputSourceManager(val eocvSim: EOCVSim) { + + companion object { + private val BLACK = Scalar(0.0, 0.0, 0.0, 255.0) + } + + @Volatile var lastMatFromSource: Mat? = null + @Volatile var currentInputSource: InputSource? = null + + val sources = mutableMapOf() + + val inputSourceLoader = InputSourceLoader() + + val onSourcesListChange = EventHandler("InputSourceManager-OnSourcesListChange") + + private var defaultSource = "" + + private val logger by loggerForThis() + + fun init() { + logger.info("Initializing...") + + if (lastMatFromSource == null) { + lastMatFromSource = Mat(Size(640.0, 480.0), 24) // 24 is CV_8UC4 (RGBA) + lastMatFromSource!!.setTo(BLACK) + } + + val size = Size(640.0, 480.0) + + createDefaultImgInputSource("/images/ug_4.jpg", "ug_eocvsim_4.jpg", "Ultimate Goal 4 Ring", size) + createDefaultImgInputSource("/images/ug_1.jpg", "ug_eocvsim_1.jpg", "Ultimate Goal 1 Ring", size) + createDefaultImgInputSource("/images/ug_0.jpg", "ug_eocvsim_0.jpg", "Ultimate Goal 0 Ring", size) + + if (sources.isEmpty()) { + logger.warn("No input sources found, creating default null source") + + val nullSource = NullSource().apply { + isDefault = true + } + + addInputSource("Default", nullSource) + } else { + setInputSource("Ultimate Goal 4 Ring", true) + } + + inputSourceLoader.loadInputSourcesFromFile() + + for ((name, source) in inputSourceLoader.loadedInputSources) { + logger.info("Loaded input source $name") + addInputSource(name, source) + } + } + + private fun createDefaultImgInputSource(resourcePath: String, fileName: String, sourceName: String, imgSize: Size) { + try { + val `is` = InputSource::class.java.getResourceAsStream(resourcePath) + val f = SysUtil.copyFileIsTemp(`is`, fileName, true).file + + val src = ImageSource(f.absolutePath, imgSize).apply { + isDefault = true + createdOn = sources.size.toLong() + } + + addInputSource(sourceName, src) + } catch (e: IOException) { + logger.error("Error while creating default image input source", e) + } + } + + fun update(isPaused: Boolean) { + val currentSource = currentInputSource ?: return + + try { + currentSource.setPaused(isPaused) + + val m = currentSource.update() + + if (m != null && !m.empty()) { + lastMatFromSource?.apply { + setTo(BLACK) // clear previous mat + m.copyTo(this) + // add an extra alpha channel because that's what eocv returns for some reason... (more realistic simulation lol) + Imgproc.cvtColor(this, this, Imgproc.COLOR_RGB2RGBA) + } + } + } catch (ex: Exception) { + logger.error("Error while processing current source", ex) + logger.warn("Changing to default source") + + setInputSource(defaultSource) + } + } + + @JvmOverloads + fun addInputSource(name: String, inputSource: InputSource?, dispatchedByUser: Boolean = false) { + if (inputSource == null) return + + if (sources.containsKey(name)) return + + inputSource.eocvSim = eocvSim + + eocvSim.visualizer.sourceSelectorPanel?.allowSourceSwitching = false + + inputSource.name = name + sources[name] = inputSource + + if (inputSource.createdOn == -1L) { + inputSource.createdOn = System.currentTimeMillis() + } + + if (!inputSource.isDefault) { + inputSourceLoader.saveInputSource(name, inputSource) + inputSourceLoader.saveInputSourcesToFile() + } + + onSourcesListChange.run() + + eocvSim.visualizer.sourceSelectorPanel?.let { panel -> + SwingUtilities.invokeLater { + val sourceSelector = panel.sourceSelector + + val currentSourceIndex = sourceSelector.selectedIndex + + if (dispatchedByUser) { + val index = panel.getIndexOf(name) + + sourceSelector.selectedIndex = index + + requestSetInputSource(name) + + eocvSim.onMainUpdate.once { + eocvSim.pipelineManager.requestSetPaused(false) + pauseIfImageTwoFrames() + } + } else { + sourceSelector.selectedIndex = currentSourceIndex + } + + panel.allowSourceSwitching = true + } + } + + logger.info("Adding InputSource $inputSource (${inputSource.javaClass.simpleName})") + } + + fun deleteInputSource(sourceName: String) { + val src = sources[sourceName] ?: return + if (src.isDefault) return + + sources.remove(sourceName) + + inputSourceLoader.deleteInputSource(sourceName) + inputSourceLoader.saveInputSourcesToFile() + } + + fun setInputSource(sourceName: String?, makeDefault: Boolean): Boolean { + val result = setInputSource(sourceName) + + if (result && makeDefault) { + defaultSource = sourceName ?: "" + } + + return result + } + + fun setInputSource(sourceName: String?): Boolean { + val src = if (sourceName == null) { + NullSource() + } else { + sources[sourceName] + } + + src?.reset() + + if (src != null) { + if (!InputSourceInitializer.initializeWithTimeout(src, this)) { + eocvSim.visualizer.asyncPleaseWaitDialog( + "Error while loading requested source", "Falling back to previous source", + "Close", Dimension(300, 150), true, true + ) + + logger.error("Error while loading requested source ($sourceName) reported by itself (init method returned false)") + + return false + } + } + + currentInputSource?.reset() + currentInputSource = src + + // if pause on images option is turned on by user + if (eocvSim.configManager.config.pauseOnImages) { + pauseIfImage() + } + + logger.info("Set InputSource to ${currentInputSource.toString()} (${src?.javaClass?.simpleName})") + + return true + } + + fun cleanSourceIfDirty() { + currentInputSource?.cleanIfDirty() + } + + fun isNameInUse(name: String) = sources.containsKey(name) + + fun tryName(name: String): String { + var sourceName = name + var count = 0 + + while (isNameInUse(sourceName)) { + count++ + sourceName = "$name ($count)" + } + + return sourceName + } + + fun pauseIfImage() { + val source = currentInputSource ?: return + + // if the new input source is an image, we will pause the next frame + // to execute one shot analysis on images and save resources. + if (SourceType.fromClass(source.javaClass) == SourceType.IMAGE) { + eocvSim.onMainUpdate.once { + eocvSim.pipelineManager.setPaused( + true, + PipelineManager.PauseReason.IMAGE_ONE_ANALYSIS + ) + } + } + } + + fun pauseIfImageTwoFrames() { + // if the new input source is an image, we will pause the next frame + // to execute one shot analysis on images and save resources. + eocvSim.onMainUpdate.once { pauseIfImage() } + } + + fun requestSetInputSource(name: String?) { + eocvSim.onMainUpdate.once { setInputSource(name) } + } + + fun showApwdIfNeeded(sourceName: String?, job: Job?): Visualizer.AsyncPleaseWaitDialog? { + val type = getSourceType(sourceName) + if (type == SourceType.CAMERA || type == SourceType.VIDEO || type == SourceType.HTTP) { + val apwd = eocvSim.visualizer.asyncPleaseWaitDialog( + "Opening source...", null, "Cancel", + Dimension(300, 150), true + ) + + apwd.onCancel { + job?.cancel(CancellationException()) + } + return apwd + } + + return null + } + + fun getDefaultInputSource() = defaultSource + + fun getSourceType(sourceName: String?): SourceType { + if (sourceName == null) return SourceType.UNKNOWN + + val source = sources[sourceName] ?: return SourceType.UNKNOWN + return SourceType.fromClass(source.javaClass) + } + + val sortedInputSources: List get() { + val sourcesList = ArrayList(sources.values) + sourcesList.sort() + + return sourcesList + } +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt index 5585acfd..c2bb5afc 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt @@ -91,7 +91,8 @@ class PipelineManager( val pipelineOutputPosters = ArrayList() val pipelineFpsCounter = FpsCounter() - private var hasInitCurrentPipeline = false + var hasInitCurrentPipeline = false + private set var lastPipelineAction = "processFrame" private set @@ -325,7 +326,7 @@ class PipelineManager( pipelineStatisticsCalculator.newPipelineFrameStart() //run our pipeline in the background until it finishes or gets cancelled - val pipelineJob = GlobalScope.launch(currentPipelineContext!!) { + val pipelineJob = eocvSim.scope.launch(currentPipelineContext!!) { try { //if we have a pipeline, we run it right here, passing the input mat //given to us. we'll post the frame the pipeline returns as long @@ -451,7 +452,7 @@ class PipelineManager( //similar to pipeline processFrame, call the user function in the background //and wait for some X timeout for the user to finisih doing what it has to do. - val viewportTappedJob = GlobalScope.launch(currentPipelineContext ?: EmptyCoroutineContext) { + val viewportTappedJob = eocvSim.scope.launch(currentPipelineContext ?: EmptyCoroutineContext) { pipeline.onViewportTapped() } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt index 9f8819f6..b7a6582b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt @@ -85,9 +85,25 @@ class CompiledPipelineManager(private val pipelineManager: PipelineManager) { private set val workspaceManager get() = pipelineManager.eocvSim.workspaceManager + val eocvSim get() = pipelineManager.eocvSim fun init() { logger.info("Initializing...") + + onBuildStart { + eocvSim.onMainUpdate.once { + eocvSim.visualizer.menuBar.workspCompile.isEnabled = false + eocvSim.visualizer.pipelineSelectorPanel.buttonsPanel.pipelineCompileBtt.isEnabled = false + } + } + + onBuildEnd { + eocvSim.onMainUpdate.once { + eocvSim.visualizer.menuBar.workspCompile.isEnabled = true + eocvSim.visualizer.pipelineSelectorPanel.buttonsPanel.pipelineCompileBtt.isEnabled = true + } + } + asyncBuild() workspaceManager.onWorkspaceChange { @@ -179,7 +195,7 @@ class CompiledPipelineManager(private val pipelineManager: PipelineManager) { onBuildEnd.callRightAway = true onBuildEnd.run() - GlobalScope.launch { + eocvSim.scope.launch { delay(1000) onBuildEnd.callRightAway = false } @@ -218,10 +234,19 @@ class CompiledPipelineManager(private val pipelineManager: PipelineManager) { @OptIn(DelicateCoroutinesApi::class) fun asyncBuild( endCallback: (PipelineCompileResult) -> Unit = {} - ) = GlobalScope.launch(Dispatchers.IO) { - endCallback(build()) + ) = eocvSim.scope.launch(Dispatchers.IO) { + if(PipelineCompiler.IS_USABLE) { + endCallback(build()) + } else { + eocvSim.onMainUpdate.once { + eocvSim.visualizer.compilerUnsupported() + } + } } + val isCompilerSupported get() = PipelineCompiler.IS_USABLE + + private fun deleteJarFile() { if(PIPELINES_OUTPUT_JAR.exists()) PIPELINES_OUTPUT_JAR.delete() currentPipelineClassLoader = null diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt index d5035a4f..fd2fce6a 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt @@ -45,6 +45,6 @@ class EOCVSimApiImpl(owner: EOCVSimPlugin, val internalEOCVSim: EOCVSim) : EOCVS override val configApi: ConfigApi by apiField(ConfigApiImpl(owner, internalEOCVSim.configManager)) override fun disableApi() { - logger.info("EOCV-Sim API for {} says: \"aight, time to check out\"", ownerName) + logger.info("EOCV-Sim API for {} says: \"ight, imma head out\"", ownerName) } } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt index 937df9c1..75597454 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt @@ -206,8 +206,8 @@ class ClasspathScan { * and store the result in [scanResult] */ @OptIn(DelicateCoroutinesApi::class) - fun asyncScan() { - scanResultJob = GlobalScope.launch(Dispatchers.IO) { + fun asyncScan(scope: CoroutineScope = GlobalScope) { + scanResultJob = scope.launch(Dispatchers.IO) { scan() } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt index 4f0c756d..eaafc921 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt @@ -230,7 +230,7 @@ class WorkspaceManager(val eocvSim: EOCVSim) { folder: File, template: WorkspaceTemplate, finishCallback: (() -> Unit)? = null - ) = GlobalScope.launch(Dispatchers.IO) { + ) = eocvSim.scope.launch(Dispatchers.IO) { if(!folder.isDirectory) return@launch if(!template.extractToIfEmpty(folder)) return@launch @@ -238,7 +238,7 @@ class WorkspaceManager(val eocvSim: EOCVSim) { workspaceFile = folder if(finishCallback != null) finishCallback() - eocvSim.visualizer.asyncCompilePipelines() + eocvSim.pipelineManager.compiledPipelineManager.asyncBuild() } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/VSCodeLauncher.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/VSCodeLauncher.kt index e877b274..bd830fab 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/VSCodeLauncher.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/VSCodeLauncher.kt @@ -24,7 +24,7 @@ package com.github.serivesmejia.eocvsim.workspace.util import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import com.github.serivesmejia.eocvsim.util.SysUtil @@ -64,6 +64,6 @@ object VSCodeLauncher { * Runs in a coroutine in the IO dispatcher context */ @OptIn(DelicateCoroutinesApi::class) - fun asyncLaunch(workspace: File) = GlobalScope.launch(Dispatchers.IO) { launch(workspace) } + fun asyncLaunch(workspace: File, scope: CoroutineScope) = scope.launch(Dispatchers.IO) { launch(workspace) } } \ No newline at end of file diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt b/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt index 300e15af..cdb12386 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt +++ b/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt @@ -68,7 +68,8 @@ class SwingOpenCvViewport(size: Size, fpsMeterDescriptor: String = "deltacv Visi PAUSED } - private val visionPreviewFrameQueue = EvictingBlockingQueue(ArrayBlockingQueue(VISION_PREVIEW_FRAME_QUEUE_CAPACITY + 1)) + private val visionPreviewFrameQueue = + EvictingBlockingQueue(ArrayBlockingQueue(VISION_PREVIEW_FRAME_QUEUE_CAPACITY + 1)) private var framebufferRecycler: MatRecycler? = null @Volatile @@ -93,11 +94,11 @@ class SwingOpenCvViewport(size: Size, fpsMeterDescriptor: String = "deltacv Visi framebufferRecycler!!.returnMat(value) } - skiaLayer.renderDelegate = SkiaLayerRenderDelegate(skiaLayer, object: SkikoRenderDelegate { + skiaLayer.renderDelegate = SkiaLayerRenderDelegate(skiaLayer, object : SkikoRenderDelegate { override fun onRender(canvas: org.jetbrains.skia.Canvas, width: Int, height: Int, nanoTime: Long) { renderCanvas(Canvas(canvas, width, height)) - if(outputPosters.isNotEmpty()) { + if (outputPosters.isNotEmpty()) { synchronized(outputPosters) { skiaLayer.screenshot().use { bmp -> framebufferRecycler?.takeMatOrNull().let { mat -> @@ -206,12 +207,6 @@ class SwingOpenCvViewport(size: Size, fpsMeterDescriptor: String = "deltacv Visi override fun post(mat: Mat, userContext: Any) { synchronized(syncObj) { - //did they give us null? - requireNotNull(mat) { - //ugh, they did - "cannot post null mat!" - } - //Are we actually rendering to the display right now? If not, //no need to waste time doing a memcpy if (internalRenderingState == RenderingState.ACTIVE) { @@ -276,7 +271,8 @@ class SwingOpenCvViewport(size: Size, fpsMeterDescriptor: String = "deltacv Visi /* * We only need to start the render thread if it's * stopped. - */if (internalRenderingState == RenderingState.STOPPED) { + */ + if (internalRenderingState == RenderingState.STOPPED) { logger.info("CheckState(): activating viewport") internalRenderingState = RenderingState.PAUSED internalRenderingState = if (userRequestedPause) { @@ -290,7 +286,8 @@ class SwingOpenCvViewport(size: Size, fpsMeterDescriptor: String = "deltacv Visi } if (internalRenderingState != RenderingState.STOPPED) { if (userRequestedPause && internalRenderingState != RenderingState.PAUSED - || !userRequestedPause && internalRenderingState != RenderingState.ACTIVE) { + || !userRequestedPause && internalRenderingState != RenderingState.ACTIVE + ) { internalRenderingState = if (userRequestedPause) { logger.info("CheckState(): pausing viewport") RenderingState.PAUSED @@ -312,12 +309,12 @@ class SwingOpenCvViewport(size: Size, fpsMeterDescriptor: String = "deltacv Visi private lateinit var lastFrame: MatRecycler.RecyclableMat private fun renderCanvas(canvas: Canvas) { - if(!::lastFrame.isInitialized) { + if (!::lastFrame.isInitialized) { lastFrame = framebufferRecycler!!.takeMatOrNull() } synchronized(canvasLock) { - if(dark) { + if (dark) { canvas.drawColor(Color.BLACK) } else { canvas.drawColor(Color.WHITE) @@ -355,11 +352,7 @@ class SwingOpenCvViewport(size: Size, fpsMeterDescriptor: String = "deltacv Visi * destroyed, calls checkState(), which *SHOULD* block until we die. This * works most of the time, but not always? We don't yet understand... */ - if (canvas != null) { - renderer.render(mat, canvas, renderHook, mat.context) - } else { - logger.info("Canvas was null") - } + renderer.render(mat, canvas, renderHook, mat.context) //We're done with that Mat object; return it to the Mat recycler so it can be used again later if (mat !== lastFrame) { @@ -370,17 +363,7 @@ class SwingOpenCvViewport(size: Size, fpsMeterDescriptor: String = "deltacv Visi RenderingState.PAUSED -> { if (shouldPaintOrange) { shouldPaintOrange = false - - /* - * For some reason, the canvas will very occasionally be null upon closing. - * Stack Overflow seems to suggest this means the canvas has been destroyed. - * However, surfaceDestroyed(), which is called right before the surface is - * destroyed, calls checkState(), which *SHOULD* block until we die. This - * works most of the time, but not always? We don't yet understand... - */ - if (canvas != null) { - renderer.renderPaused(canvas) - } + renderer.renderPaused(canvas) } } @@ -410,6 +393,7 @@ class SwingOpenCvViewport(size: Size, fpsMeterDescriptor: String = "deltacv Visi companion object { private const val VISION_PREVIEW_FRAME_QUEUE_CAPACITY = 2 - private const val FRAMEBUFFER_RECYCLER_CAPACITY = VISION_PREVIEW_FRAME_QUEUE_CAPACITY + 4 //So that the evicting queue can be full, and the render thread has one checked out (+1) and post() can still take one (+1). + private const val FRAMEBUFFER_RECYCLER_CAPACITY = + VISION_PREVIEW_FRAME_QUEUE_CAPACITY + 4 //So that the evicting queue can be full, and the render thread has one checked out (+1) and post() can still take one (+1). } } \ No newline at end of file From 724fc5b25a1143a422b248f5fc4082499e25bfde Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Wed, 15 Apr 2026 21:52:39 -0600 Subject: [PATCH 02/40] Refactor the tunable field api into a cleaner implementation --- .../component/tuner/TunableFieldPanel.java | 126 +++++----- .../tuner/TunableFieldPanelOptions.kt | 5 +- .../tuner/element/TunableComboBox.java | 35 ++- .../component/tuner/element/TunableSlider.kt | 22 +- .../tuner/element/TunableTextField.java | 35 +-- .../eocvsim/pipeline/PipelineManager.kt | 7 +- .../plugin/api/impl/VariableTunerApiImpl.kt | 8 +- .../eocvsim/tuner/TunableField.java | 155 ------------ .../eocvsim/tuner/TunableField.kt | 99 ++++++++ .../tuner/TunableFieldAcceptorManager.kt | 30 --- .../eocvsim/tuner/TunableFieldRegistry.kt | 95 +++++++ .../eocvsim/tuner/TunableValue.kt | 95 +++++++ .../eocvsim/tuner/TunerManager.java | 233 ------------------ .../eocvsim/tuner/TunerManager.kt | 96 ++++++++ .../eocvsim/tuner/field/BooleanField.java | 108 -------- .../eocvsim/tuner/field/BooleanField.kt | 36 +++ .../eocvsim/tuner/field/EnumField.kt | 54 ++-- .../eocvsim/tuner/field/NumericField.java | 85 ------- .../eocvsim/tuner/field/NumericField.kt | 41 +++ .../eocvsim/tuner/field/StringField.java | 103 -------- .../eocvsim/tuner/field/StringField.kt | 37 +++ .../eocvsim/tuner/field/cv/PointField.java | 128 ---------- .../eocvsim/tuner/field/cv/PointField.kt | 44 ++++ .../eocvsim/tuner/field/cv/RectField.kt | 84 ++----- .../eocvsim/tuner/field/cv/ScalarField.java | 136 ---------- .../eocvsim/tuner/field/cv/ScalarField.kt | 45 ++++ .../tuner/field/numeric/DoubleField.java | 61 ----- .../tuner/field/numeric/DoubleField.kt | 24 ++ .../tuner/field/numeric/FloatField.java | 61 ----- .../eocvsim/tuner/field/numeric/FloatField.kt | 24 ++ .../tuner/field/numeric/IntegerField.java | 61 ----- .../tuner/field/numeric/IntegerField.kt | 24 ++ .../tuner/field/numeric/LongField.java | 61 ----- .../eocvsim/tuner/field/numeric/LongField.kt | 24 ++ .../tuner/scanner/RegisterTunableField.java | 34 --- .../scanner/RegisterTunableFieldAcceptor.java | 38 --- .../eocvsim/util/ClasspathScan.kt | 42 +--- 37 files changed, 858 insertions(+), 1538 deletions(-) delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldAcceptorManager.kt create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldRegistry.kt create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableValue.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/scanner/RegisterTunableField.java delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/scanner/RegisterTunableFieldAcceptor.java diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.java index 87857b52..3fb03727 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.java @@ -27,11 +27,12 @@ import com.github.serivesmejia.eocvsim.gui.component.tuner.element.TunableComboBox; import com.github.serivesmejia.eocvsim.gui.component.tuner.element.TunableSlider; import com.github.serivesmejia.eocvsim.gui.component.tuner.element.TunableTextField; -import com.github.serivesmejia.eocvsim.tuner.TunableField; +import com.github.serivesmejia.eocvsim.tuner.*; import javax.swing.*; import javax.swing.border.SoftBevelBorder; import java.awt.*; +import java.util.List; @SuppressWarnings("unchecked") public class TunableFieldPanel extends JPanel { @@ -68,12 +69,21 @@ public TunableFieldPanel(TunableField tunableField, EOCVSim eocvSim) { } private void init() { - //nice look setBorder(new SoftBevelBorder(SoftBevelBorder.RAISED)); panelOptions = new TunableFieldPanelOptions(this, eocvSim); - if(tunableField.getGuiFieldAmount() > 0) { + List> values = tunableField.getTunableValues(); + + boolean hasFieldsOrSliders = false; + for (TunableValue val : values) { + if (val instanceof TunableNumber || val instanceof TunableString) { + hasFieldsOrSliders = true; + break; + } + } + + if(hasFieldsOrSliders) { add(panelOptions); } @@ -82,67 +92,69 @@ private void init() { add(fieldNameLabel); - int fieldAmount = tunableField.getGuiFieldAmount(); - - fields = new TunableTextField[fieldAmount]; - sliders = new TunableSlider[fieldAmount]; + fields = new TunableTextField[values.size()]; + sliders = new TunableSlider[values.size()]; + comboBoxes = new JComboBox[values.size()]; fieldsPanel = new JPanel(); slidersPanel = new JPanel(new GridBagLayout()); - - for (int i = 0 ; i < tunableField.getGuiFieldAmount() ; i++) { - //add the tunable field as a field - TunableTextField field = new TunableTextField(i, tunableField, eocvSim); - fields[i] = field; - - field.setEditable(true); - fieldsPanel.add(field); - - //add the tunable field as a slider - JLabel sliderLabel = new JLabel("0"); - TunableSlider slider = new TunableSlider(i, tunableField, eocvSim, sliderLabel); - sliders[i] = slider; - - GridBagConstraints cSlider = new GridBagConstraints(); - cSlider.gridx = 0; - cSlider.gridy = i; - - GridBagConstraints cLabel = new GridBagConstraints(); - cLabel.gridy = 1; - cLabel.gridy = i; - - slidersPanel.add(slider, cSlider); - slidersPanel.add(sliderLabel, cLabel); + + for (int i = 0 ; i < values.size() ; i++) { + TunableValue value = values.get(i); + + if (value instanceof TunableNumber || value instanceof TunableString) { + TunableTextField field = new TunableTextField(value, eocvSim); + fields[i] = field; + field.setEditable(true); + fieldsPanel.add(field); + + if (value instanceof TunableNumber) { + JLabel sliderLabel = new JLabel("0"); + TunableSlider slider = new TunableSlider((TunableNumber) value, eocvSim, sliderLabel); + sliders[i] = slider; + + GridBagConstraints cSlider = new GridBagConstraints(); + cSlider.gridx = 0; + cSlider.gridy = i; + + GridBagConstraints cLabel = new GridBagConstraints(); + cLabel.gridx = 1; + cLabel.gridy = i; + + slidersPanel.add(slider, cSlider); + slidersPanel.add(sliderLabel, cLabel); + } + } else if (value instanceof TunableEnum) { + TunableComboBox comboBox = new TunableComboBox((TunableEnum) value, eocvSim); + add(comboBox); + comboBoxes[i] = comboBox; + } } setMode(Mode.TEXTBOXES); - - comboBoxes = new JComboBox[tunableField.getGuiComboBoxAmount()]; - - for (int i = 0; i < comboBoxes.length; i++) { - TunableComboBox comboBox = new TunableComboBox(i, tunableField, eocvSim); - add(comboBox); - - comboBoxes[i] = comboBox; - } } - //method that should be called when this panel is added to the visualizer gui public void showFieldPanel() { if(hasBeenShown) return; hasBeenShown = true; - //updates the slider ranges from config panelOptions.getConfigPanel().updateFieldGuiFromConfig(); - tunableField.evalRecommendedPanelMode(); + if (!tunableField.isOnlyNumbers()) { + setMode(Mode.SLIDERS); + } } public void setFieldValue(int index, Object value) { if(index >= fields.length) return; - if(fields[index].isFocusOwner() || sliders[index].isFocusOwner()) return; // don't update if the field is being edited + if(fields[index] == null) return; + + if(fields[index].isFocusOwner()) return; + if(sliders[index] != null && sliders[index].isFocusOwner()) return; String text; - if(tunableField.getAllowMode() == TunableField.AllowMode.ONLY_NUMBERS) { + TunableValue tv = (TunableValue) tunableField.getTunableValues().get(index); + + if(tv instanceof TunableNumber && ((TunableNumber)tv).isOnlyNumbers()) { text = String.valueOf((int) Math.round(Double.parseDouble(value.toString()))); } else { text = value.toString(); @@ -151,12 +163,14 @@ public void setFieldValue(int index, Object value) { fields[index].setText(text); try { - sliders[index].setScaledValue(Double.parseDouble(value.toString())); + if (sliders[index] != null) sliders[index].setScaledValue(Double.parseDouble(value.toString())); } catch(NumberFormatException ignored) {} } public void setComboBoxSelection(int index, Object selection) { - comboBoxes[index].setSelectedItem(selection.toString()); + if (comboBoxes[index] != null) { + comboBoxes[index].setSelectedItem(selection.toString()); + } } protected void requestAllConfigReeval() { @@ -170,10 +184,10 @@ public void setMode(Mode mode) { remove(slidersPanel); } - for(int i = 0 ; i < tunableField.getGuiFieldAmount() ; i++) { - fields[i].setInControl(true); - sliders[i].setInControl(false); - setFieldValue(i, tunableField.getGuiFieldValue(i)); + for(int i = 0 ; i < fields.length ; i++) { + if (fields[i] != null) fields[i].setInControl(true); + if (sliders[i] != null) sliders[i].setInControl(false); + if (fields[i] != null) setFieldValue(i, ((TunableValue) tunableField.getTunableValues().get(i)).getValue()); } add(fieldsPanel); @@ -183,10 +197,10 @@ public void setMode(Mode mode) { remove(fieldsPanel); } - for(int i = 0 ; i < tunableField.getGuiFieldAmount() ; i++) { - fields[i].setInControl(false); - sliders[i].setInControl(true); - setFieldValue(i, tunableField.getGuiFieldValue(i)); + for(int i = 0 ; i < fields.length ; i++) { + if (fields[i] != null) fields[i].setInControl(false); + if (sliders[i] != null) sliders[i].setInControl(true); + if (fields[i] != null) setFieldValue(i, ((TunableValue) tunableField.getTunableValues().get(i)).getValue()); } add(slidersPanel); @@ -205,7 +219,7 @@ public void setMode(Mode mode) { public void setSlidersRange(double min, double max) { if(sliders == null) return; for(TunableSlider slider : sliders) { - slider.setScaledBounds(min, max); + if (slider != null) slider.setScaledBounds(min, max); } } 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 20a3e28a..94acc1d2 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 @@ -149,8 +149,9 @@ class TunableFieldPanelOptions(val fieldPanel: TunableFieldPanel, //if we're still in range of the scalar values amount if(i < colorScalar.`val`.size) { val colorVal = colorScalar.`val`[i] - fieldPanel.setFieldValue(i, colorVal) - fieldPanel.tunableField.setFieldValueFromGui(i, colorVal.toString()) + + val tv = fieldPanel.tunableField.tunableValues.getOrNull(i) as? com.github.serivesmejia.eocvsim.tuner.TunableNumber + tv?.setFromGui(colorVal) } else { break } //keep looping until we write the entire scalar value } colorPickButton.isSelected = false diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableComboBox.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableComboBox.java index 8a542aa9..36f23fe4 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableComboBox.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableComboBox.java @@ -24,40 +24,53 @@ package com.github.serivesmejia.eocvsim.gui.component.tuner.element; import com.github.serivesmejia.eocvsim.EOCVSim; +import com.github.serivesmejia.eocvsim.tuner.TunableEnum; import com.github.serivesmejia.eocvsim.tuner.TunableField; import javax.swing.*; import java.util.Objects; +@SuppressWarnings({"unchecked", "rawtypes"}) public class TunableComboBox extends JComboBox { - private final TunableField tunableField; - private final int index; + private final TunableEnum tunableValue; private final EOCVSim eocvSim; - public TunableComboBox(int index, TunableField tunableField, EOCVSim eocvSim) { + public TunableComboBox(TunableEnum tunableValue, EOCVSim eocvSim) { super(); - this.tunableField = tunableField; - this.index = index; + this.tunableValue = tunableValue; this.eocvSim = eocvSim; init(); } private void init() { - for (Object obj : tunableField.getGuiComboBoxValues(index)) { + for (Object obj : tunableValue.getEnumValues()) { this.addItem(obj.toString()); } addItemListener(evt -> eocvSim.onMainUpdate.once(() -> { - try { - tunableField.setComboBoxValueFromGui(index, Objects.requireNonNull(getSelectedItem()).toString()); - } catch (IllegalAccessException ignored) {} + if (evt.getStateChange() == java.awt.event.ItemEvent.SELECTED) { + Object[] values = tunableValue.getEnumValues(); + Object selected = null; + String selectedStr = Objects.requireNonNull(getSelectedItem()).toString(); + + for (Object val : values) { + if (val.toString().equals(selectedStr)) { + selected = val; + break; + } + } + + if (selected != null) { + ((TunableEnum) tunableValue).setFromGui((Enum) selected); + } - if (eocvSim.pipelineManager.getPaused()) { - eocvSim.pipelineManager.requestSetPaused(false); + if (eocvSim.pipelineManager.getPaused()) { + eocvSim.pipelineManager.requestSetPaused(false); + } } })); } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableSlider.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableSlider.kt index b64252c1..4b640b55 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableSlider.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableSlider.kt @@ -25,13 +25,11 @@ package com.github.serivesmejia.eocvsim.gui.component.tuner.element import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.gui.component.SliderX -import com.github.serivesmejia.eocvsim.tuner.TunableField -import com.github.serivesmejia.eocvsim.util.event.EventListener +import com.github.serivesmejia.eocvsim.tuner.TunableNumber import javax.swing.JLabel import kotlin.math.roundToInt -class TunableSlider(val index: Int, - val tunableField: TunableField<*>, +class TunableSlider(val tunableValue: TunableNumber, val eocvSim: EOCVSim, val valueLabel: JLabel? = null, minBound: Double = 0.0, @@ -39,33 +37,31 @@ class TunableSlider(val index: Int, var inControl = false - constructor(i: Int, tunableField: TunableField, eocvSim: EOCVSim, valueLabel: JLabel) : this(i, tunableField, eocvSim, valueLabel, 0.0, 255.0) + constructor(tunableValue: TunableNumber, eocvSim: EOCVSim, valueLabel: JLabel) : this(tunableValue, eocvSim, valueLabel, 0.0, 255.0) - constructor(i: Int, tunableField: TunableField, eocvSim: EOCVSim) : this(i, tunableField, eocvSim, null, 0.0, 255.0) + constructor(tunableValue: TunableNumber, eocvSim: EOCVSim) : this(tunableValue, eocvSim, null, 0.0, 255.0) init { addChangeListener { eocvSim.onMainUpdate.once { - if(!tunableField.shouldIgnoreGuiUpdates() && inControl) { - tunableField.setFieldValueFromGui(index, scaledValue.toString()) + if(inControl) { + tunableValue.setFromGui(scaledValue) if (eocvSim.pipelineManager.paused) eocvSim.pipelineManager.setPaused(false) } } - valueLabel?.text = if (tunableField.allowMode == TunableField.AllowMode.ONLY_NUMBERS_DECIMAL) { + valueLabel?.text = if (!tunableValue.isOnlyNumbers) { scaledValue.toString() } else { scaledValue.roundToInt().toString() } } - tunableField.onValueChange { + tunableValue.onValueChange { if (!inControl) { - scaledValue = try { - tunableField.getGuiFieldValue(index).toString().toDouble() - } catch(_: NumberFormatException) { 0.0 } + scaledValue = tunableValue.value } } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.java index 4d7013f8..b103d14e 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.java @@ -24,7 +24,9 @@ package com.github.serivesmejia.eocvsim.gui.component.tuner.element; import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.tuner.TunableField; +import com.github.serivesmejia.eocvsim.tuner.TunableNumber; +import com.github.serivesmejia.eocvsim.tuner.TunableString; +import com.github.serivesmejia.eocvsim.tuner.TunableValue; import javax.swing.*; import javax.swing.border.Border; @@ -45,8 +47,7 @@ public class TunableTextField extends JTextField { private final ArrayList validCharsIfNumber = new ArrayList<>(); - private final TunableField tunableField; - private final int index; + private final TunableValue tunableValue; private final EOCVSim eocvSim; private final Border initialBorder; @@ -55,33 +56,35 @@ public class TunableTextField extends JTextField { private boolean inControl = false; - public TunableTextField(int index, TunableField tunableField, EOCVSim eocvSim) { + public TunableTextField(TunableValue tunableValue, EOCVSim eocvSim) { super(); this.initialBorder = this.getBorder(); - this.tunableField = tunableField; - this.index = index; + this.tunableValue = tunableValue; this.eocvSim = eocvSim; - setText(tunableField.getGuiFieldValue(index).toString()); + setText(tunableValue.getValue().toString()); int plusW = Math.round(getText().length() / 5f) * 10; this.setPreferredSize(new Dimension(40 + plusW, getPreferredSize().height)); - tunableField.onValueChange.attach(() -> { + tunableValue.getOnValueChange().attach(() -> { if(!inControl) { - setText(tunableField.getGuiFieldValue(index).toString()); + setText(tunableValue.getValue().toString()); } }); - if (tunableField.isOnlyNumbers()) { + boolean isNumber = tunableValue instanceof TunableNumber; + + if (isNumber) { + TunableNumber numValue = (TunableNumber) tunableValue; //add all valid characters for non decimal numeric fields Collections.addAll(validCharsIfNumber, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-'); //allow dots for decimal numeric fields - if (tunableField.getAllowMode() == TunableField.AllowMode.ONLY_NUMBERS_DECIMAL) { + if (!numValue.isOnlyNumbers()) { validCharsIfNumber.add('.'); } @@ -121,13 +124,15 @@ public void replace(FilterBypass fb, int offset, int length, String text, Attrib getDocument().addDocumentListener(new DocumentListener() { Runnable changeFieldValue = () -> { - if(tunableField.shouldIgnoreGuiUpdates()) return; - String text = getText(); - if ((!hasValidText || !tunableField.isOnlyNumbers() || (text != null && !getText().trim().equals("")))) { + if ((!hasValidText || !isNumber || (text != null && !getText().trim().equals("")))) { try { - tunableField.setFieldValueFromGui(index, getText()); + if (isNumber) { + ((TunableNumber) tunableValue).setFromGui(Double.parseDouble(text)); + } else if (tunableValue instanceof TunableString) { + ((TunableString) tunableValue).setFromGui(text); + } } catch (Exception e) { setRedBorder(); } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt index c2bb5afc..3900a30b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt @@ -32,6 +32,7 @@ import com.github.serivesmejia.eocvsim.pipeline.instantiator.PipelineInstantiato import com.github.serivesmejia.eocvsim.pipeline.instantiator.processor.ProcessorInstantiator import com.github.serivesmejia.eocvsim.pipeline.util.PipelineExceptionTracker import com.github.serivesmejia.eocvsim.pipeline.util.PipelineSnapshot +import com.github.serivesmejia.eocvsim.tuner.TunableFieldRegistry import com.github.serivesmejia.eocvsim.util.ReflectUtil import com.github.serivesmejia.eocvsim.util.StrUtil import com.github.serivesmejia.eocvsim.util.SysUtil @@ -111,10 +112,6 @@ class PipelineManager( var previousPipelineIndex = 0 var virtualReflect: VirtualReflection = JvmVirtualReflection - set(value) { - eocvSim.tunerManager.setVirtualReflection(value) - field = value - } var reflectTarget: Any? = null private set @@ -160,7 +157,7 @@ class PipelineManager( // when getTunableFieldOf returns null, it means that // it wasn't able to find a suitable TunableField for // the passed Field type. - eocvSim.tunerManager.getTunableFieldClassOf(it) != null + TunableFieldRegistry.hasTunableFieldFor(it.type) } //manages and builds pipelines in runtime diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VariableTunerApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VariableTunerApiImpl.kt index 38fd0c8d..469afa57 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VariableTunerApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VariableTunerApiImpl.kt @@ -24,6 +24,7 @@ package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.tuner.TunableField +import com.github.serivesmejia.eocvsim.tuner.TunableNumber import com.github.serivesmejia.eocvsim.tuner.TunerManager import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin import io.github.deltacv.eocvsim.plugin.api.TunableFieldApi @@ -33,8 +34,8 @@ import io.github.deltacv.eocvsim.virtualreflect.VirtualField class TunableFieldApiImpl(owner: EOCVSimPlugin, val internalTunableField: TunableField<*>) : TunableFieldApi(owner) { override val field: VirtualField by liveApiField { internalTunableField.reflectionField } - override fun setFieldValue(index: Int, value: Any) = apiImpl { - internalTunableField.setFieldValue(index, value) + override fun setFieldValue(index: Int, value: Any) = apiImpl { + internalTunableField.tunableValues.getOrNull(index)?.setAnyFromGui(value) } override fun disableApi() { } @@ -45,7 +46,8 @@ class VariableTunerApiImpl(owner: EOCVSimPlugin, val internalTunerManager: Tuner virtualField: VirtualField, pipeline: Any ) = apiImpl { - val tunableField = internalTunerManager.newTunableFieldInstanceFor(virtualField, pipeline) + val tunableField = internalTunerManager.newTunableFieldInstanceFor(virtualField, pipeline) + if (tunableField == null) return@apiImpl null TunableFieldApiImpl(owner, tunableField) } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.java deleted file mode 100644 index d5389c7f..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.tuner; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel; -import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanelConfig; -import com.github.serivesmejia.eocvsim.util.event.EventHandler; -import io.github.deltacv.eocvsim.virtualreflect.VirtualField; - -public abstract class TunableField { - - protected VirtualField reflectionField; - protected TunableFieldPanel fieldPanel; - - protected Object target; - protected AllowMode allowMode; - protected EOCVSim eocvSim; - - protected Object initialFieldValue; - - private int guiFieldAmount = 1; - private int guiComboBoxAmount = 0; - - private boolean ignoreGuiUpdates = false; - - public final EventHandler onValueChange = new EventHandler("TunableField-ValueChange"); - - private TunableFieldPanel.Mode recommendedMode = null; - - public TunableField(Object target, VirtualField reflectionField, EOCVSim eocvSim, AllowMode allowMode) throws IllegalAccessException { - this.reflectionField = reflectionField; - this.target = target; - this.allowMode = allowMode; - this.eocvSim = eocvSim; - - initialFieldValue = reflectionField.get(); - } - - public TunableField(Object target, VirtualField reflectionField, EOCVSim eocvSim) throws IllegalAccessException { - this(target, reflectionField, eocvSim, AllowMode.TEXT); - } - - public abstract void init(); - - public abstract void update(); - - public abstract void updateGuiFieldValues(); - - public void setPipelineFieldValue(T newValue) throws IllegalAccessException { - if (hasChanged()) { //execute if value is not the same to save resources - reflectionField.set(newValue); - onValueChange.run(); - } - } - - public void setIgnoreGuiUpdates(boolean ignore) { - ignoreGuiUpdates = ignore; - } - - public abstract void setFieldValue(int index, Object newValue) throws IllegalAccessException; - public abstract void setFieldValueFromGui(int index, String newValue) throws IllegalAccessException; - - public void setComboBoxValueFromGui(int index, String newValue) throws IllegalAccessException { } - - public final void setTunableFieldPanel(TunableFieldPanel fieldPanel) { - this.fieldPanel = fieldPanel; - } - - protected final void setRecommendedPanelMode(TunableFieldPanel.Mode mode) { - recommendedMode = mode; - } - - public final void evalRecommendedPanelMode() { - TunableFieldPanelConfig configPanel = fieldPanel.panelOptions.getConfigPanel(); - TunableFieldPanelConfig.ConfigSource configSource = configPanel.getLocalConfig().getSource(); - //only apply the recommendation if user hasn't - //configured a global or specific field config - if(recommendedMode != null && fieldPanel != null && configSource == TunableFieldPanelConfig.ConfigSource.GLOBAL_DEFAULT) { - fieldPanel.setMode(recommendedMode); - } - } - - public abstract T getValue(); - - public abstract Object getGuiFieldValue(int index); - - public Object[] getGuiComboBoxValues(int index) { - return new Object[0]; - } - - public final int getGuiFieldAmount() { - return guiFieldAmount; - } - public final void setGuiFieldAmount(int amount) { - this.guiFieldAmount = amount; - } - - public final int getGuiComboBoxAmount() { - return guiComboBoxAmount; - } - public final void setGuiComboBoxAmount(int amount) { - this.guiComboBoxAmount = amount; - } - - public final String getFieldName() { - return reflectionField.getName(); - } - public final String getFieldTypeName() { - return reflectionField.getType().getSimpleName(); - } - - public final AllowMode getAllowMode() { - return allowMode; - } - - public final VirtualField getReflectionField() { - return reflectionField; - } - - public final boolean isOnlyNumbers() { - return getAllowMode() == TunableField.AllowMode.ONLY_NUMBERS || - getAllowMode() == TunableField.AllowMode.ONLY_NUMBERS_DECIMAL; - } - - public boolean shouldIgnoreGuiUpdates() { - return ignoreGuiUpdates; - } - - public abstract boolean hasChanged(); - - public enum AllowMode {ONLY_NUMBERS, ONLY_NUMBERS_DECIMAL, TEXT} - -} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.kt new file mode 100644 index 00000000..1465926a --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.kt @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2021 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.tuner + +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel +import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanelConfig +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import io.github.deltacv.eocvsim.virtualreflect.VirtualField + +abstract class TunableField( + protected val target: Any, + val reflectionField: VirtualField, + protected val eocvSim: EOCVSim, + val allowMode: AllowMode = AllowMode.TEXT +) { + var fieldPanel: TunableFieldPanel? = null + private set + + protected val initialFieldValue: Any? = reflectionField.get() + + var isIgnoreGuiUpdates: Boolean = false + + @JvmField + val onValueChange = EventHandler("TunableField-ValueChange") + + abstract fun init() + + open fun update() { + refreshPipelineObject() + for (tunableValue in tunableValues) { + tunableValue.update() + } + } + + open fun refreshPipelineObject() {} + + open fun setPipelineFieldValue(newValue: T) { + val current = reflectionField.get() + if (current != newValue) { + reflectionField.set(newValue) + onValueChange.run() + } + } + + abstract val tunableValues: List> + + fun setTunableFieldPanel(fieldPanel: TunableFieldPanel) { + this.fieldPanel = fieldPanel + + for ((index, tunableValue) in tunableValues.withIndex()) { + tunableValue.onPipelineUpdate.attach { + if (!isIgnoreGuiUpdates) { + javax.swing.SwingUtilities.invokeLater { + fieldPanel.setFieldValue(index, tunableValue.value) + } + } + } + } + } + + abstract val value: T + + val fieldName: String + get() = reflectionField.name + + val fieldTypeName: String + get() = reflectionField.type.simpleName + + val isOnlyNumbers: Boolean + get() = allowMode == AllowMode.ONLY_NUMBERS || allowMode == AllowMode.ONLY_NUMBERS_DECIMAL + + fun shouldIgnoreGuiUpdates(): Boolean = isIgnoreGuiUpdates + + enum class AllowMode { + ONLY_NUMBERS, ONLY_NUMBERS_DECIMAL, TEXT + } +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldAcceptorManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldAcceptorManager.kt deleted file mode 100644 index e887b5f0..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldAcceptorManager.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.github.serivesmejia.eocvsim.tuner - -import io.github.deltacv.common.util.loggerForThis -import java.util.HashMap - -class TunableFieldAcceptorManager(private val acceptors: HashMap>, Class>) { - - val logger by loggerForThis() - - fun accept(clazz: Class<*>): Class>? { - for((fieldClass, acceptorClass) in acceptors) { - //try getting a constructor for this acceptor - val acceptorConstructor = try { - acceptorClass.getConstructor() //get constructor with no params - } catch(ex: NoSuchMethodException) { - logger.error("TunableFieldAcceptor ${acceptorClass.typeName} doesn't implement a constructor with zero parameters", ex) - continue - } - - val acceptor = acceptorConstructor.newInstance() //create an instance of this acceptor - - if(acceptor.accept(clazz)) { //try accepting the given clazz type - return fieldClass //wooo someone accepted our type! return to tell who did. - } - } - - return null //no one accepted our type... poor clazz :( - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldRegistry.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldRegistry.kt new file mode 100644 index 00000000..fa8653eb --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldRegistry.kt @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.tuner + +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.util.ReflectUtil +import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import com.github.serivesmejia.eocvsim.tuner.field.numeric.* +import com.github.serivesmejia.eocvsim.tuner.field.* +import com.github.serivesmejia.eocvsim.tuner.field.cv.* + +/** + * A manual registry for tunable fields, eliminating the need for classpath scanning + * and complex reflection constructor invocation. + */ +object TunableFieldRegistry { + + private val exactFields = mutableMapOf, (Any, VirtualField, EOCVSim) -> TunableField<*>>() + private val acceptors = mutableListOf TunableField<*>>>() + + init { + registerField(Int::class.javaObjectType) { target, f, sim -> IntegerField(target, f, sim) } + registerField(Double::class.javaObjectType) { target, f, sim -> DoubleField(target, f, sim) } + registerField(Float::class.javaObjectType) { target, f, sim -> FloatField(target, f, sim) } + registerField(Long::class.javaObjectType) { target, f, sim -> LongField(target, f, sim) } + + registerField(String::class.java) { target, f, sim -> StringField(target, f, sim) } + registerField(Boolean::class.javaObjectType) { target, f, sim -> BooleanField(target, f, sim) } + + registerField(org.opencv.core.Scalar::class.java) { target, f, sim -> ScalarField(target, f, sim) } + registerField(org.opencv.core.Point::class.java) { target, f, sim -> PointField(target, f, sim) } + registerField(org.opencv.core.Rect::class.java) { target, f, sim -> RectField(target, f, sim) } + + registerAcceptor(EnumField.Acceptor()) { target, f, sim -> EnumField(target, f, sim) } + } + + fun registerField(type: Class<*>, constructor: (Any, VirtualField, EOCVSim) -> TunableField<*>) { + exactFields[type] = constructor + } + + fun registerAcceptor(acceptor: TunableFieldAcceptor, constructor: (Any, VirtualField, EOCVSim) -> TunableField<*>) { + acceptors.add(acceptor to constructor) + } + + fun getTunableFieldFor(field: VirtualField, pipeline: Any, eocvSim: EOCVSim): TunableField<*>? { + if (field.isFinal) return null + + var type = field.type + if (type.isPrimitive) { + type = ReflectUtil.wrap(type) + } + + var constructor = exactFields[type] + if (constructor == null) { + constructor = acceptors.find { it.first.accept(type) }?.second + } + + return constructor?.invoke(pipeline, field, eocvSim) + } + + fun hasTunableFieldFor(type: Class<*>): Boolean { + var t = type + if (t.isPrimitive) { + t = ReflectUtil.wrap(t) + } + if (exactFields.containsKey(t)) return true + return acceptors.any { it.first.accept(t) } + } + + fun reset() { + exactFields.clear() + acceptors.clear() + } +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableValue.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableValue.kt new file mode 100644 index 00000000..641f8563 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableValue.kt @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.tuner + +import com.github.serivesmejia.eocvsim.util.event.EventHandler + +sealed class TunableValue( + initialValue: T, + val supplier: () -> T, + val consumer: (T) -> Unit +) { + private var _value: T = initialValue + + var value: T + get() = _value + set(newValue) { + if (_value != newValue) { + _value = newValue + onValueChange.run() + } + } + + val onValueChange = EventHandler("TunableValue-ValueChange") + val onPipelineUpdate = EventHandler("TunableValue-PipelineUpdate") + + fun setFromPipeline(newValue: T) { + if (_value != newValue) { + _value = newValue + onPipelineUpdate.run() + } + } + + fun update() { + setFromPipeline(supplier()) + } + + fun setFromGui(guiValue: T) { + consumer(guiValue) + value = guiValue + } + + abstract fun setAnyFromGui(guiValue: Any) +} + +class TunableNumber(initialValue: Double, supplier: () -> Double, consumer: (Double) -> Unit, val isOnlyNumbers: Boolean = false) : TunableValue(initialValue, supplier, consumer) { + override fun setAnyFromGui(guiValue: Any) { + if (guiValue is Number) setFromGui(guiValue.toDouble()) + else throw IllegalArgumentException("Expected Number but got ${guiValue::class.java.name}") + } +} +class TunableString(initialValue: String, supplier: () -> String, consumer: (String) -> Unit) : TunableValue(initialValue, supplier, consumer) { + override fun setAnyFromGui(guiValue: Any) { + if (guiValue is String) setFromGui(guiValue) + else throw IllegalArgumentException("Expected String but got ${guiValue::class.java.name}") + } +} +class TunableBoolean(initialValue: Boolean, supplier: () -> Boolean, consumer: (Boolean) -> Unit) : TunableValue(initialValue, supplier, consumer) { + override fun setAnyFromGui(guiValue: Any) { + if (guiValue is Boolean) setFromGui(guiValue) + else throw IllegalArgumentException("Expected Boolean but got ${guiValue::class.java.name}") + } +} +class TunableEnum>(initialValue: T, val enumValues: Array, supplier: () -> T, consumer: (T) -> Unit) : TunableValue(initialValue, supplier, consumer) { + override fun setAnyFromGui(guiValue: Any) { + if (value != null) { + if (value!!::class.java.isInstance(guiValue)) { + @Suppress("UNCHECKED_CAST") + setFromGui(guiValue as T) + } else { + throw IllegalArgumentException("Expected ${value!!::class.java.name} but got ${guiValue::class.java.name}") + } + } + } +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.java deleted file mode 100644 index bfe4340d..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.tuner; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel; -import com.github.serivesmejia.eocvsim.tuner.exception.CancelTunableFieldAddingException; -import com.github.serivesmejia.eocvsim.util.ReflectUtil; -import io.github.deltacv.eocvsim.virtualreflect.VirtualField; -import io.github.deltacv.eocvsim.virtualreflect.VirtualReflectContext; -import io.github.deltacv.eocvsim.virtualreflect.VirtualReflection; -import io.github.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflection; -import org.jetbrains.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.lang.reflect.*; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -@SuppressWarnings("rawtypes") -public class TunerManager { - - Logger logger = LoggerFactory.getLogger(getClass()); - - private final EOCVSim eocvSim; - - private final List fields = new ArrayList<>(); - - private TunableFieldAcceptorManager acceptorManager = null; - - private static HashMap>> tunableFieldsTypes = null; - private static HashMap>, Class> tunableFieldAcceptors = null; - - private VirtualReflection reflect = JvmVirtualReflection.INSTANCE; - - private boolean firstInit = true; - - public TunerManager(EOCVSim eocvSim) { - this.eocvSim = eocvSim; - } - - public void init() { - if(tunableFieldsTypes == null) { - tunableFieldsTypes = new HashMap<>(); - // ... - for(Class> clazz : eocvSim.getClasspathScan().getScanResult().getTunableFieldClasses()) { - tunableFieldsTypes.put(ReflectUtil.getTypeArgumentsFrom(clazz)[0], clazz); - } - } - - if(tunableFieldAcceptors == null) { - tunableFieldAcceptors = new HashMap<>(); - // oh god... - tunableFieldAcceptors.putAll(eocvSim.getClasspathScan().getScanResult().getTunableFieldAcceptorClasses()); - } - - // for some reason, acceptorManager becomes null after a certain time passes - // (maybe garbage collected? i don't know for sure...), but we can simply recover - // from this by creating a new one with the found acceptors by the scanner, no problem. - if(acceptorManager == null) - acceptorManager = new TunableFieldAcceptorManager(tunableFieldAcceptors); - - if (firstInit) { - eocvSim.pipelineManager.onPipelineChange.attach(this::reset); - firstInit = false; - } - - if (eocvSim.pipelineManager.getReflectTarget() != null) { - addFieldsFrom(eocvSim.pipelineManager.getReflectTarget()); - eocvSim.visualizer.updateTunerFields(createTunableFieldPanels()); - - for(TunableField field : fields.toArray(new TunableField[0])) { - try { - field.init(); - } catch(CancelTunableFieldAddingException e) { - logger.info("Field " + field.getFieldName() + " was removed due to \"" + e.getMessage() + "\""); - fields.remove(field); - } - } - } - } - - public void update() { - //update all fields - for(TunableField field : fields.toArray(new TunableField[0])) { - try { - field.update(); - } catch(Exception ex) { - logger.error("Error while updating field " + field.getFieldName(), ex); - } - - //check if this field has requested to reevaluate config for all panels - if(field.fieldPanel.hasRequestedAllConfigReeval()) { - //if so, iterate through all fields to reevaluate - for(TunableField f : fields.toArray(new TunableField[0])) { - f.fieldPanel.panelOptions.reevaluateConfig(); - } - } - } - } - - public void reset() { - fields.clear(); - init(); - } - - @SuppressWarnings("unchecked") - public TunableField newTunableFieldInstanceFor(VirtualField field, Object pipeline) { - Class tunableFieldClass = getTunableFieldClassOf(field); - - if(tunableFieldClass != null) { - //yay we have a registered TunableField which handles this. - //now, lets do some more reflection to instantiate this TunableField - try { - Constructor constructor = tunableFieldClass.getConstructor(Object.class, VirtualField.class, EOCVSim.class); - return (TunableField) constructor.newInstance(pipeline, field, eocvSim); - } catch(InvocationTargetException e) { - if(e.getCause() instanceof CancelTunableFieldAddingException) { - String message = e.getCause().getMessage(); - logger.info("Field {} wasn't added due to \"{}\"", field.getName(), message); - } else { - logger.error("Reflection error while processing field: {}", field.getName(), e); - } - } catch (Exception ex) { - //oops rip - logger.error("Reflection error while processing field: {}", field.getName(), ex); - } - } - - return null; - } - - public Class getTunableFieldClassOf(VirtualField field) { - //we only accept non-final fields - if (field.isFinal()) return null; - - Class type = field.getType(); - if (field.getType().isPrimitive()) { //wrap to java object equivalent if field type is primitive - type = ReflectUtil.wrap(type); - } - - Class tunableFieldClass = null; - - if(tunableFieldsTypes.containsKey(type)) { - tunableFieldClass = tunableFieldsTypes.get(type); - } else { - //if we don't have a class yet, use our acceptors - if(acceptorManager != null) tunableFieldClass = acceptorManager.accept(type); - } - - return tunableFieldClass; - } - - public void addFieldsFrom(Object pipeline) { - if (pipeline == null) return; - - VirtualReflectContext reflectContext = reflect.contextOf(pipeline); - if(reflectContext == null) return; - - VirtualField[] fields = reflect.contextOf(pipeline).getFields(); - - for (VirtualField field : fields) { - // ...add it to the list - TunableField tunableField = newTunableFieldInstanceFor(field, pipeline); - - if(tunableField != null) { - this.fields.add(tunableField); - } - } - } - - @Nullable public TunableField getCurrentTunableFieldWithLabel(String label) { - TunableField labeledField = null; - - for(TunableField field : fields) { - String fieldLabel = field.reflectionField.getLabel(); - - if(fieldLabel != null && fieldLabel.equals(label)) { - labeledField = field; - break; - } - } - - if(labeledField != null) { - labeledField.setIgnoreGuiUpdates(true); - } - return labeledField; - } - - public void setVirtualReflection(VirtualReflection reflect) { - this.reflect = reflect; - } - - public void reevaluateConfigs() { - for(TunableField field : fields) { - field.fieldPanel.panelOptions.reevaluateConfig(); - } - } - - private List createTunableFieldPanels() { - List panels = new ArrayList<>(); - - for (TunableField field : fields) { - panels.add(new TunableFieldPanel(field, eocvSim)); - } - - return panels; - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt new file mode 100644 index 00000000..08c539f5 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt @@ -0,0 +1,96 @@ +package com.github.serivesmejia.eocvsim.tuner + +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel +import com.github.serivesmejia.eocvsim.tuner.exception.CancelTunableFieldAddingException +import io.github.deltacv.common.util.loggerForThis +import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import io.github.deltacv.eocvsim.virtualreflect.VirtualReflection +import io.github.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflection + +class TunerManager(private val eocvSim: EOCVSim) { + + val logger by loggerForThis() + + val fields = mutableListOf>() + + var reflect: VirtualReflection = JvmVirtualReflection + + private var firstInit = true + + fun init() { + if (firstInit) { + eocvSim.pipelineManager.onPipelineChange.attach { reset() } + firstInit = false + } + + eocvSim.pipelineManager.reflectTarget?.let { target -> + addFieldsFrom(target) + eocvSim.visualizer.updateTunerFields(createTunableFieldPanels()) + + val iterator = fields.iterator() + while (iterator.hasNext()) { + val field = iterator.next() + try { + field.init() + } catch (e: CancelTunableFieldAddingException) { + logger.info("Field ${field.fieldName} was removed due to \"${e.message}\"") + iterator.remove() + } + } + } + } + + fun update() { + for (field in fields.toList()) { // toList to avoid concurrent modification issues + try { + field.update() + } catch (ex: Exception) { + logger.error("Error while updating field ${field.fieldName}", ex) + } + + if (field.fieldPanel?.hasRequestedAllConfigReeval() == true) { + for (f in fields) { + f.fieldPanel?.panelOptions?.reevaluateConfig() + } + } + } + } + + fun reset() { + fields.clear() + init() + } + + fun newTunableFieldInstanceFor(field: VirtualField, pipeline: Any): TunableField<*>? { + return TunableFieldRegistry.getTunableFieldFor(field, pipeline, eocvSim) + } + + fun addFieldsFrom(pipeline: Any) { + val reflectContext = reflect.contextOf(pipeline) ?: return + val virtualFields = reflectContext.fields + + for (field in virtualFields) { + val tunableField = newTunableFieldInstanceFor(field, pipeline) + if (tunableField != null) { + fields.add(tunableField) + } + } + } + + fun getCurrentTunableFieldWithLabel(label: String): TunableField<*>? { + val labeledField = fields.find { it.reflectionField.label == label } + labeledField?.isIgnoreGuiUpdates = true + return labeledField + } + + fun reevaluateConfigs() { + for (field in fields) { + field.fieldPanel?.panelOptions?.reevaluateConfig() + } + } + + private fun createTunableFieldPanels(): List { + return fields.map { TunableFieldPanel(it, eocvSim) } + } +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.java deleted file mode 100644 index bef2ee71..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.tuner.field; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.tuner.TunableField; -import com.github.serivesmejia.eocvsim.tuner.scanner.RegisterTunableField; -import io.github.deltacv.eocvsim.virtualreflect.VirtualField; - -import javax.swing.*; - -@RegisterTunableField -public class BooleanField extends TunableField { - - boolean value; - - boolean lastVal; - volatile boolean hasChanged = false; - - public BooleanField(Object instance, VirtualField reflectionField, EOCVSim eocvSim) throws IllegalAccessException { - super(instance, reflectionField, eocvSim, AllowMode.TEXT); - - setGuiFieldAmount(0); - setGuiComboBoxAmount(1); - - value = (boolean) initialFieldValue; - } - - @Override - public void init() {} - - @Override - public void update() { - hasChanged = value != lastVal; - - if (hasChanged) { //update values in GUI if they changed since last check - updateGuiFieldValues(); - } - - lastVal = value; - } - - @Override - public void updateGuiFieldValues() { - SwingUtilities.invokeLater(() -> fieldPanel.setComboBoxSelection(0, value)); - } - - @Override - public void setFieldValue(int index, Object newValue) throws IllegalAccessException { - value = (boolean) newValue; - setPipelineFieldValue((boolean)newValue); - } - - @Override - public void setFieldValueFromGui(int index, String newValue) throws IllegalAccessException { - setComboBoxValueFromGui(index, newValue); - } - - @Override - public void setComboBoxValueFromGui(int index, String newValue) throws IllegalAccessException { - value = Boolean.parseBoolean(newValue); - setFieldValue(index, value); - lastVal = value; - } - - @Override - public Boolean getValue() { - return value; - } - - @Override - public Object getGuiFieldValue(int index) { - return value; - } - - @Override - public Object[] getGuiComboBoxValues(int index) { - return new Boolean[]{value, !value}; - } - - @Override - public boolean hasChanged() { - hasChanged = value != lastVal; - return hasChanged; - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.kt new file mode 100644 index 00000000..bdeb0243 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.kt @@ -0,0 +1,36 @@ +package com.github.serivesmejia.eocvsim.tuner.field + +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.tuner.TunableBoolean +import com.github.serivesmejia.eocvsim.tuner.TunableField +import io.github.deltacv.eocvsim.virtualreflect.VirtualField + +class BooleanField( + instance: Any, + reflectionField: VirtualField, + eocvSim: EOCVSim +) : TunableField(instance, reflectionField, eocvSim, AllowMode.TEXT) { + + private var _value: Boolean = initialFieldValue as? Boolean ?: false + + private val tunableValue by lazy { TunableBoolean(_value, { _value }, { updateBoolean(it) }) } + + override val tunableValues by lazy { listOf(tunableValue) } + + private fun updateBoolean(newValue: Boolean) { + _value = newValue + setPipelineFieldValue(_value) + } + + override fun init() { + reflectionField.set(_value) + } + + override fun refreshPipelineObject() { + val current = reflectionField.get() as? Boolean ?: return + _value = current + } + + override val value: Boolean + get() = _value +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/EnumField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/EnumField.kt index 1648a4a1..6a6bc781 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/EnumField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/EnumField.kt @@ -1,13 +1,11 @@ package com.github.serivesmejia.eocvsim.tuner.field import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.tuner.TunableEnum import com.github.serivesmejia.eocvsim.tuner.TunableField import com.github.serivesmejia.eocvsim.tuner.TunableFieldAcceptor -import com.github.serivesmejia.eocvsim.tuner.scanner.RegisterTunableField import io.github.deltacv.eocvsim.virtualreflect.VirtualField -import javax.swing.SwingUtilities -@RegisterTunableField class EnumField(instance: Any, reflectionField: VirtualField, eocvSim: EOCVSim) : TunableField>(instance, reflectionField, eocvSim, AllowMode.TEXT) { @@ -17,53 +15,29 @@ class EnumField(instance: Any, private val initialValue = initialFieldValue as Enum<*> private var currentValue = initialValue - private var beforeValue: Any? = null - init { - guiComboBoxAmount = 1 - guiFieldAmount = 0 - } - - override fun init() { - fieldPanel.setComboBoxSelection(0, currentValue) - } + private val tunableValue by lazy { TunableEnum(currentValue, values as Array, { currentValue }, { updateEnum(it) }) } - override fun update() { - if(hasChanged()) { - currentValue = value - updateGuiFieldValues() - } - beforeValue = currentValue - } + override val tunableValues by lazy { listOf(tunableValue) } - override fun updateGuiFieldValues() { - SwingUtilities.invokeLater { - fieldPanel.setComboBoxSelection(0, currentValue) - } + private fun updateEnum(newValue: Enum<*>) { + currentValue = newValue + setPipelineFieldValue(newValue) } - override fun setFieldValue(index: Int, newValue: Any) { - reflectionField.set(newValue) - } - - override fun setComboBoxValueFromGui(index: Int, newValue: String) = setFieldValueFromGui(index, newValue) - - override fun setFieldValueFromGui(index: Int, newValue: String) { - currentValue = java.lang.Enum.valueOf(initialValue::class.java, newValue) - setFieldValue(index, currentValue) + override fun init() { + reflectionField.set(currentValue) } - override fun getValue() = currentValue - - override fun getGuiFieldValue(index: Int) = currentValue.name - - override fun getGuiComboBoxValues(index: Int): Array { - return values + override fun refreshPipelineObject() { + val curr = reflectionField.get() as? Enum<*> ?: return + currentValue = curr } - override fun hasChanged() = reflectionField.get() != beforeValue + override val value: Enum<*> + get() = currentValue - class EnumFieldAcceptor : TunableFieldAcceptor { + class Acceptor : TunableFieldAcceptor { override fun accept(clazz: Class<*>) = clazz.isEnum } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.java deleted file mode 100644 index 3ddaf419..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.tuner.field; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel; -import com.github.serivesmejia.eocvsim.tuner.TunableField; -import io.github.deltacv.eocvsim.virtualreflect.VirtualField; - -import javax.swing.*; - -abstract public class NumericField extends TunableField { - - protected T value; - protected T beforeValue; - - protected volatile boolean hasChanged = false; - - public NumericField(Object instance, VirtualField reflectionField, EOCVSim eocvSim, AllowMode allowMode) throws IllegalAccessException { - super(instance, reflectionField, eocvSim, allowMode); - } - - @Override - public void init() { - setRecommendedPanelMode(TunableFieldPanel.Mode.TEXTBOXES); - } - - @Override - @SuppressWarnings("unchecked") - public void update() { - if (value == null) return; - value = (T) reflectionField.get(); - - hasChanged = hasChanged(); - - if (hasChanged) { - updateGuiFieldValues(); - } - } - - @Override - public void updateGuiFieldValues() { - SwingUtilities.invokeLater(() -> fieldPanel.setFieldValue(0, value)); - } - - @Override - public T getValue() { - return value; - } - - @Override - public Object getGuiFieldValue(int index) { - return value; - } - - @Override - public boolean hasChanged() { - boolean hasChanged = value != beforeValue; - beforeValue = value; - return hasChanged; - } - - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt new file mode 100644 index 00000000..57d3e856 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt @@ -0,0 +1,41 @@ +package com.github.serivesmejia.eocvsim.tuner.field + +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel +import com.github.serivesmejia.eocvsim.tuner.TunableField +import com.github.serivesmejia.eocvsim.tuner.TunableNumber +import io.github.deltacv.eocvsim.virtualreflect.VirtualField + +abstract class NumericField( + target: Any, + reflectionField: VirtualField, + eocvSim: EOCVSim, + allowMode: AllowMode +) : TunableField(target, reflectionField, eocvSim, allowMode) { + + protected var _value: T? = initialFieldValue as T? + + protected val tunableValue by lazy { TunableNumber(_value?.toDouble() ?: 0.0, { _value?.toDouble() ?: 0.0 }, { updateNumber(it) }) } + + override val tunableValues by lazy { listOf(tunableValue) } + + abstract fun createNumber(value: Double): T + + private fun updateNumber(newValue: Double) { + _value = createNumber(newValue) + setPipelineFieldValue(_value!!) + } + + override fun init() { + reflectionField.set(_value) + } + + override fun refreshPipelineObject() { + @Suppress("UNCHECKED_CAST") + val current = reflectionField.get() as T + _value = current + } + + override val value: T + get() = _value!! +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.java deleted file mode 100644 index 6bf3d6dc..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.tuner.field; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel; -import com.github.serivesmejia.eocvsim.tuner.TunableField; -import com.github.serivesmejia.eocvsim.tuner.scanner.RegisterTunableField; -import io.github.deltacv.eocvsim.virtualreflect.VirtualField; - -import javax.swing.*; - -@RegisterTunableField -public class StringField extends TunableField { - - String value; - - String lastVal = ""; - - volatile boolean hasChanged = false; - - public StringField(Object instance, VirtualField reflectionField, EOCVSim eocvSim) throws IllegalAccessException { - super(instance, reflectionField, eocvSim, AllowMode.TEXT); - - if(initialFieldValue != null) { - value = (String) initialFieldValue; - } else { - value = ""; - } - } - - @Override - public void init() { - setRecommendedPanelMode(TunableFieldPanel.Mode.TEXTBOXES); - } - - @Override - public void update() { - hasChanged = !value.equals(lastVal); - - if (hasChanged) { //update values in GUI if they changed since last check - updateGuiFieldValues(); - } - - lastVal = value; - } - - @Override - public void updateGuiFieldValues() { - SwingUtilities.invokeLater(() -> fieldPanel.setFieldValue(0, value)); - } - - @Override - public void setFieldValue(int index, Object newValue) throws IllegalAccessException { - setPipelineFieldValue((String)newValue); - } - - @Override - public void setFieldValueFromGui(int index, String newValue) throws IllegalAccessException { - value = newValue; - setFieldValue(index, value); - - lastVal = value; - } - - @Override - public String getValue() { - return value; - } - - @Override - public Object getGuiFieldValue(int index) { - return value; - } - - @Override - public boolean hasChanged() { - hasChanged = !value.equals(lastVal); - return hasChanged; - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.kt new file mode 100644 index 00000000..4323a9db --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.kt @@ -0,0 +1,37 @@ +package com.github.serivesmejia.eocvsim.tuner.field + +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel +import com.github.serivesmejia.eocvsim.tuner.TunableField +import com.github.serivesmejia.eocvsim.tuner.TunableString +import io.github.deltacv.eocvsim.virtualreflect.VirtualField + +class StringField( + instance: Any, + reflectionField: VirtualField, + eocvSim: EOCVSim +) : TunableField(instance, reflectionField, eocvSim, AllowMode.TEXT) { + + private var _value: String = initialFieldValue as? String ?: "" + + private val tunableValue by lazy { TunableString(_value, { _value }, { updateString(it) }) } + + override val tunableValues by lazy { listOf(tunableValue) } + + private fun updateString(newValue: String) { + _value = newValue + setPipelineFieldValue(_value) + } + + override fun init() { + reflectionField.set(_value) + } + + override fun refreshPipelineObject() { + val current = reflectionField.get() as? String ?: return + _value = current + } + + override val value: String + get() = _value +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.java deleted file mode 100644 index 31262ad9..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.tuner.field.cv; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.tuner.TunableField; -import com.github.serivesmejia.eocvsim.tuner.scanner.RegisterTunableField; -import io.github.deltacv.eocvsim.virtualreflect.VirtualField; -import org.opencv.core.Point; - -import javax.swing.*; - -@RegisterTunableField -public class PointField extends TunableField { - - Point point; - - double lastX = 0; - double lastY = 0; - - volatile boolean hasChanged = false; - - public PointField(Object instance, VirtualField reflectionField, EOCVSim eocvSim) throws IllegalAccessException { - super(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL); - - if(initialFieldValue != null) { - Point p = (Point) initialFieldValue; - point = new Point(p.x, p.y); - } else { - point = new Point(0, 0); - } - - setGuiFieldAmount(2); - } - - @Override - public void init() { - reflectionField.set(point); - } - - @Override - public void update() { - hasChanged = point.x != lastX || point.y != lastY; - - if (hasChanged) { //update values in GUI if they changed since last check - updateGuiFieldValues(); - } - - lastX = point.x; - lastY = point.y; - } - - @Override - public void updateGuiFieldValues() { - SwingUtilities.invokeLater(() -> { - fieldPanel.setFieldValue(0, point.x); - fieldPanel.setFieldValue(1, point.y); - }); - } - - @Override - public void setFieldValue(int index, Object newValue) throws IllegalAccessException { - try { - double value = 0; - if(newValue instanceof String) { - value = Double.parseDouble((String)newValue); - } else { - value = (double)newValue; - } - - if (index == 0) { - point.x = value; - } else { - point.y = value; - } - } catch (Exception ex) { - throw new IllegalArgumentException("Parameter should be a valid number", ex); - } - - setPipelineFieldValue(point); - - lastX = point.x; - lastY = point.y; - } - - @Override - public void setFieldValueFromGui(int index, String newValue) throws IllegalAccessException { - setFieldValue(index, point); - } - - @Override - public Point getValue() { - return point; - } - - @Override - public Object getGuiFieldValue(int index) { - return index == 0 ? point.x : point.y; - } - - @Override - public boolean hasChanged() { - hasChanged = point.x != lastX || point.y != lastY; - return hasChanged; - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.kt new file mode 100644 index 00000000..d1d95597 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.kt @@ -0,0 +1,44 @@ +package com.github.serivesmejia.eocvsim.tuner.field.cv + +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.tuner.TunableField +import com.github.serivesmejia.eocvsim.tuner.TunableNumber +import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.opencv.core.Point + +class PointField( + instance: Any, + reflectionField: VirtualField, + eocvSim: EOCVSim +) : TunableField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL) { + + private var point: Point = if (initialFieldValue != null) { + val p = initialFieldValue as Point + Point(p.x, p.y) + } else { + Point(0.0, 0.0) + } + + private val xValue by lazy { TunableNumber(point.x, { point.x }, { updatePoint(0, it) }) } + private val yValue by lazy { TunableNumber(point.y, { point.y }, { updatePoint(1, it) }) } + + override val tunableValues by lazy { listOf(xValue, yValue) } + + private fun updatePoint(index: Int, newValue: Double) { + if (index == 0) point.x = newValue else point.y = newValue + setPipelineFieldValue(point) + } + + override fun init() { + reflectionField.set(point) + } + + override fun refreshPipelineObject() { + val current = reflectionField.get() as Point + point.x = current.x + point.y = current.y + } + + override val value: Point + get() = point +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/RectField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/RectField.kt index 83c1ed12..5916f570 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/RectField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/RectField.kt @@ -25,88 +25,52 @@ package com.github.serivesmejia.eocvsim.tuner.field.cv import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.tuner.TunableField -import com.github.serivesmejia.eocvsim.tuner.scanner.RegisterTunableField -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import com.github.serivesmejia.eocvsim.tuner.TunableNumber import org.opencv.core.Rect -import javax.swing.SwingUtilities +import io.github.deltacv.eocvsim.virtualreflect.VirtualField -@RegisterTunableField class RectField(instance: Any, reflectionField: VirtualField, eocvSim: EOCVSim) : TunableField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL) { private var rect = arrayOf(0.0, 0.0, 0.0, 0.0) - private var lastRect = arrayOf(0.0, 0.0, 0.0, 0.0) - - @Volatile private var hasChanged = false private var initialRect = if(initialFieldValue != null) (initialFieldValue as Rect).clone() else Rect(0, 0, 0, 0) + private val xValue by lazy { TunableNumber(rect[0], { rect[0] }, { updateRect(0, it) }) } + private val yValue by lazy { TunableNumber(rect[1], { rect[1] }, { updateRect(1, it) }) } + private val wValue by lazy { TunableNumber(rect[2], { rect[2] }, { updateRect(2, it) }) } + private val hValue by lazy { TunableNumber(rect[3], { rect[3] }, { updateRect(3, it) }) } + + private fun updateRect(index: Int, newValue: Double) { + rect[index] = newValue + initialRect.set(rect.toDoubleArray()) + setPipelineFieldValue(initialRect) + } + + override val tunableValues by lazy { listOf(xValue, yValue, wValue, hValue) } + init { rect[0] = initialRect.x.toDouble() rect[1] = initialRect.y.toDouble() rect[2] = initialRect.width.toDouble() rect[3] = initialRect.height.toDouble() - - guiFieldAmount = 4 } override fun init() { reflectionField.set(initialRect) } - override fun update() { - if(hasChanged()){ - initialRect = reflectionField.get() as Rect + override fun refreshPipelineObject() { + initialRect = reflectionField.get() as Rect - rect[0] = initialRect.x.toDouble() - rect[1] = initialRect.y.toDouble() - rect[2] = initialRect.width.toDouble() - rect[3] = initialRect.height.toDouble() - - updateGuiFieldValues() - } - } - - override fun setFieldValue(index: Int, newValue: Any) { - try { - rect[index] = if(newValue is String) - newValue.toDouble() - else (newValue as Number).toDouble() - } catch (e: Exception) { - throw IllegalArgumentException("Parameter should be a valid numeric value", e) - } - - initialRect.set(rect.toDoubleArray()) - setPipelineFieldValue(initialRect) - - lastRect[0] = initialRect.x.toDouble() - lastRect[1] = initialRect.y.toDouble() - lastRect[2] = initialRect.width.toDouble() - lastRect[3] = initialRect.height.toDouble() - } - - override fun updateGuiFieldValues() { - SwingUtilities.invokeLater { - for((i, value) in rect.withIndex()) { - fieldPanel.setFieldValue(i, value) - } - } - } - - override fun setFieldValueFromGui(index: Int, newValue: String) { - setFieldValue(index, newValue) - } - - override fun getValue(): Rect = Rect(rect.toDoubleArray()) - - override fun getGuiFieldValue(index: Int): Any = rect[index] - - override fun hasChanged(): Boolean { - hasChanged = rect[0] != lastRect[0] || rect[1] != lastRect[1] - || rect[2] != lastRect[2] || rect[3] != lastRect[3] - return hasChanged + rect[0] = initialRect.x.toDouble() + rect[1] = initialRect.y.toDouble() + rect[2] = initialRect.width.toDouble() + rect[3] = initialRect.height.toDouble() } -} \ No newline at end of file + override val value: Rect + get() = Rect(rect.toDoubleArray()) +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.java deleted file mode 100644 index 6d2a7b4a..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.tuner.field.cv; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel; -import com.github.serivesmejia.eocvsim.tuner.TunableField; -import com.github.serivesmejia.eocvsim.tuner.scanner.RegisterTunableField; -import io.github.deltacv.eocvsim.virtualreflect.VirtualField; -import org.opencv.core.Scalar; - -import javax.swing.*; -import java.util.Arrays; - -@RegisterTunableField -public class ScalarField extends TunableField { - - int scalarSize; - Scalar scalar; - - double[] lastVal = {0, 0, 0, 0}; - - volatile boolean hasChanged = false; - - public ScalarField(Object instance, VirtualField reflectionField, EOCVSim eocvSim) throws IllegalAccessException { - super(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL); - - if(initialFieldValue == null) { - scalar = new Scalar(0, 0, 0); - } else { - scalar = ((Scalar) initialFieldValue).clone(); - } - - scalarSize = scalar.val.length; - - setGuiFieldAmount(4); - setRecommendedPanelMode(TunableFieldPanel.Mode.SLIDERS); - } - - @Override - public void init() { - reflectionField.set(scalar); - } - - @Override - public void update() { - scalar = (Scalar) reflectionField.get(); - - hasChanged = !Arrays.equals(scalar.val, lastVal); - - if (hasChanged) { //update values in GUI if they changed since last check - updateGuiFieldValues(); - } - - lastVal[0] = scalar.val[0]; - lastVal[1] = scalar.val[1]; - lastVal[2] = scalar.val[2]; - lastVal[3] = scalar.val[3]; - } - - @Override - public void updateGuiFieldValues() { - SwingUtilities.invokeLater(() -> { - for (int i = 0; i < scalar.val.length; i++) { - fieldPanel.setFieldValue(i, scalar.val[i]); - } - }); - } - - @Override - public void setFieldValue(int index, Object newValue) throws IllegalAccessException { - try { - double value; - - if(newValue instanceof String) { - value = Double.parseDouble((String) newValue); - } else { - value = (double)newValue; - } - - scalar.val[index] = value; - } catch (NumberFormatException ex) { - throw new IllegalArgumentException("Parameter should be a valid number", ex); - } - - setPipelineFieldValue(scalar); - - lastVal[0] = scalar.val[0]; - lastVal[1] = scalar.val[1]; - lastVal[2] = scalar.val[2]; - lastVal[3] = scalar.val[3]; - } - - @Override - public void setFieldValueFromGui(int index, String newValue) throws IllegalAccessException { - setFieldValue(index, newValue); - } - - @Override - public Scalar getValue() { - return scalar; - } - - @Override - public Object getGuiFieldValue(int index) { - return scalar.val[index]; - } - - @Override - public boolean hasChanged() { - hasChanged = !Arrays.equals(scalar.val, lastVal); - return hasChanged; - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.kt new file mode 100644 index 00000000..c8b9f6d1 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.kt @@ -0,0 +1,45 @@ +package com.github.serivesmejia.eocvsim.tuner.field.cv + +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel +import com.github.serivesmejia.eocvsim.tuner.TunableField +import com.github.serivesmejia.eocvsim.tuner.TunableNumber +import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.opencv.core.Scalar + +class ScalarField( + instance: Any, + reflectionField: VirtualField, + eocvSim: EOCVSim +) : TunableField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL) { + + private var scalar: Scalar = if (initialFieldValue == null) { + Scalar(0.0, 0.0, 0.0) + } else { + (initialFieldValue as Scalar).clone() + } + + private val val0 by lazy { TunableNumber(scalar.`val`[0], { scalar.`val`[0] }, { updateScalar(0, it) }) } + private val val1 by lazy { TunableNumber(scalar.`val`[1], { scalar.`val`[1] }, { updateScalar(1, it) }) } + private val val2 by lazy { TunableNumber(scalar.`val`[2], { scalar.`val`[2] }, { updateScalar(2, it) }) } + private val val3 by lazy { TunableNumber(scalar.`val`[3], { scalar.`val`[3] }, { updateScalar(3, it) }) } + + override val tunableValues by lazy { listOf(val0, val1, val2, val3) } + + private fun updateScalar(index: Int, newValue: Double) { + scalar.`val`[index] = newValue + setPipelineFieldValue(scalar) + } + + override fun init() { + reflectionField.set(scalar) + } + + override fun refreshPipelineObject() { + val current = reflectionField.get() as Scalar + scalar = current + } + + override val value: Scalar + get() = scalar +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.java deleted file mode 100644 index e06c6e37..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.tuner.field.numeric; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.tuner.field.NumericField; -import com.github.serivesmejia.eocvsim.tuner.scanner.RegisterTunableField; -import io.github.deltacv.eocvsim.virtualreflect.VirtualField; - -@RegisterTunableField -public class DoubleField extends NumericField { - - public DoubleField(Object instance, VirtualField reflectionField, EOCVSim eocvSim) throws IllegalAccessException { - super(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL); - value = (double) initialFieldValue; - } - - @Override - public void setFieldValueFromGui(int index, String newValue) throws IllegalAccessException { - try { - value = Double.valueOf(newValue); - } catch (NumberFormatException ex) { - throw new IllegalArgumentException("Parameter should be a valid numeric String"); - } - - setFieldValue(index, value); - beforeValue = value; - } - - @Override - public void setFieldValue(int index, Object value) throws IllegalAccessException { - if(value instanceof Number) { - this.value = ((Number) value).doubleValue(); - } else { - this.value = (double)value; - } - setPipelineFieldValue(this.value); - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt new file mode 100644 index 00000000..6e274adf --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt @@ -0,0 +1,24 @@ +package com.github.serivesmejia.eocvsim.tuner.field.numeric + +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.tuner.field.NumericField +import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import com.github.serivesmejia.eocvsim.tuner.TunableFieldAcceptor + +class DoubleField( + instance: Any, + reflectionField: VirtualField, + eocvSim: EOCVSim +) : NumericField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL) { + + init { + _value = initialFieldValue as? Double ?: 0.0 + } + + override fun createNumber(value: Double): Double = value + + class Acceptor : TunableFieldAcceptor { + override fun accept(clazz: Class<*>) = + clazz == Double::class.java || clazz == java.lang.Double::class.java + } +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.java deleted file mode 100644 index c995a0b7..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.tuner.field.numeric; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.tuner.field.NumericField; -import com.github.serivesmejia.eocvsim.tuner.scanner.RegisterTunableField; -import io.github.deltacv.eocvsim.virtualreflect.VirtualField; - -@RegisterTunableField -public class FloatField extends NumericField { - - public FloatField(Object instance, VirtualField reflectionField, EOCVSim eocvSim) throws IllegalAccessException { - super(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL); - value = (float) initialFieldValue; - } - - @Override - public void setFieldValueFromGui(int index, String newValue) throws IllegalAccessException { - try { - value = Float.parseFloat(newValue); - } catch (NumberFormatException ex) { - throw new IllegalArgumentException("Parameter should be a valid numeric String"); - } - - setFieldValue(index, value); - beforeValue = value; - } - - @Override - public void setFieldValue(int index, Object value) throws IllegalAccessException { - if(value instanceof Number) { - this.value = ((Number) value).floatValue(); - } else { - this.value = (float)value; - } - setPipelineFieldValue(this.value); - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt new file mode 100644 index 00000000..b4c308c6 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt @@ -0,0 +1,24 @@ +package com.github.serivesmejia.eocvsim.tuner.field.numeric + +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.tuner.field.NumericField +import com.github.serivesmejia.eocvsim.tuner.TunableFieldAcceptor +import io.github.deltacv.eocvsim.virtualreflect.VirtualField + +class FloatField( + instance: Any, + reflectionField: VirtualField, + eocvSim: EOCVSim +) : NumericField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL) { + + init { + _value = initialFieldValue as? Float ?: 0.0f + } + + override fun createNumber(value: Double): Float = value.toFloat() + + class Acceptor : TunableFieldAcceptor { + override fun accept(clazz: Class<*>) = + clazz == Float::class.java || clazz == java.lang.Float::class.java + } +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.java deleted file mode 100644 index f260cb2c..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.tuner.field.numeric; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.tuner.field.NumericField; -import com.github.serivesmejia.eocvsim.tuner.scanner.RegisterTunableField; -import io.github.deltacv.eocvsim.virtualreflect.VirtualField; - -@RegisterTunableField -public class IntegerField extends NumericField { - - public IntegerField(Object instance, VirtualField reflectionField, EOCVSim eocvSim) throws IllegalAccessException { - super(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS); - value = (int) initialFieldValue; - } - - @Override - public void setFieldValueFromGui(int index, String newValue) throws IllegalAccessException { - try { - value = (int) Math.round(Double.parseDouble(newValue)); - } catch (NumberFormatException ex) { - throw new IllegalArgumentException("Parameter should be a valid numeric String"); - } - - setFieldValue(index, value); - beforeValue = value; - } - - @Override - public void setFieldValue(int index, Object value) throws IllegalAccessException { - if(value instanceof Number) { - this.value = ((Number) value).intValue(); - } else { - this.value = (int)value; - } - setPipelineFieldValue(this.value); - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt new file mode 100644 index 00000000..3c6b8c80 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt @@ -0,0 +1,24 @@ +package com.github.serivesmejia.eocvsim.tuner.field.numeric + +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.tuner.field.NumericField +import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import com.github.serivesmejia.eocvsim.tuner.TunableFieldAcceptor + +class IntegerField( + instance: Any, + reflectionField: VirtualField, + eocvSim: EOCVSim +) : NumericField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS) { + + init { + _value = initialFieldValue as? Int ?: 0 + } + + override fun createNumber(value: Double): Int = value.toInt() + + class Acceptor : TunableFieldAcceptor { + override fun accept(clazz: Class<*>) = + clazz == Int::class.java || clazz == java.lang.Integer::class.java + } +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.java deleted file mode 100644 index b5fcde37..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.tuner.field.numeric; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.tuner.field.NumericField; -import com.github.serivesmejia.eocvsim.tuner.scanner.RegisterTunableField; -import io.github.deltacv.eocvsim.virtualreflect.VirtualField; - -@RegisterTunableField -public class LongField extends NumericField { - - public LongField(Object instance, VirtualField reflectionField, EOCVSim eocvSim) throws IllegalAccessException { - super(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS); - value = (long) initialFieldValue; - } - - @Override - public void setFieldValueFromGui(int index, String newValue) throws IllegalAccessException { - try { - value = Math.round(Double.parseDouble(newValue)); - } catch (NumberFormatException ex) { - throw new IllegalArgumentException("Parameter should be a valid numeric String"); - } - - setFieldValue(index, value); - beforeValue = value; - } - - @Override - public void setFieldValue(int index, Object value) throws IllegalAccessException { - if(value instanceof Number) { - this.value = ((Number) value).longValue(); - } else { - this.value = (long)value; - } - setPipelineFieldValue(this.value); - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt new file mode 100644 index 00000000..f8705ba3 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt @@ -0,0 +1,24 @@ +package com.github.serivesmejia.eocvsim.tuner.field.numeric + +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.tuner.field.NumericField +import com.github.serivesmejia.eocvsim.tuner.TunableFieldAcceptor +import io.github.deltacv.eocvsim.virtualreflect.VirtualField + +class LongField( + instance: Any, + reflectionField: VirtualField, + eocvSim: EOCVSim +) : NumericField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS) { + + init { + _value = initialFieldValue as? Long ?: 0L + } + + override fun createNumber(value: Double): Long = value.toLong() + + class Acceptor : TunableFieldAcceptor { + override fun accept(clazz: Class<*>) = + clazz == Long::class.java || clazz == java.lang.Long::class.java + } +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/scanner/RegisterTunableField.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/scanner/RegisterTunableField.java deleted file mode 100644 index 48e2bfd0..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/scanner/RegisterTunableField.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.tuner.scanner; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.TYPE; - -@Target({TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface RegisterTunableField { } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/scanner/RegisterTunableFieldAcceptor.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/scanner/RegisterTunableFieldAcceptor.java deleted file mode 100644 index d692c53e..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/scanner/RegisterTunableFieldAcceptor.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.tuner.scanner; - -import com.github.serivesmejia.eocvsim.tuner.TunableField; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.TYPE; - -@Target({TYPE}) -@Retention(RetentionPolicy.RUNTIME) -public @interface RegisterTunableFieldAcceptor { - Class> tunableFieldType(); -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt index 75597454..9227a281 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt @@ -23,10 +23,7 @@ package com.github.serivesmejia.eocvsim.util -import com.github.serivesmejia.eocvsim.tuner.TunableField -import com.github.serivesmejia.eocvsim.tuner.TunableFieldAcceptor -import com.github.serivesmejia.eocvsim.tuner.scanner.RegisterTunableField -import com.qualcomm.robotcore.eventloop.opmode.Disabled + import com.qualcomm.robotcore.eventloop.opmode.Disabled import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode import com.qualcomm.robotcore.eventloop.opmode.OpMode import com.qualcomm.robotcore.util.ElapsedTime @@ -100,7 +97,6 @@ class ClasspathScan { logger.info("ClassGraph finished scanning (took ${timer.seconds()}s)") - val tunableFieldClassesInfo = scanResult.getClassesWithAnnotation(RegisterTunableField::class.java.name) val pipelineClasses = mutableListOf>() @@ -164,38 +160,10 @@ class ClasspathScan { logger.info("Found ${pipelineClasses.size} pipelines") - val tunableFieldClasses = mutableListOf>>() - val tunableFieldAcceptorClasses = mutableMapOf>, Class>() - - for(tunableFieldClassInfo in tunableFieldClassesInfo) { - val clazz = if(classLoader != null) { - classLoader.loadClass(tunableFieldClassInfo.name) - } else Class.forName(tunableFieldClassInfo.name) - - if(ReflectUtil.hasSuperclass(clazz, TunableField::class.java)) { - val tunableFieldClass = clazz as Class> - - tunableFieldClasses.add(tunableFieldClass) - logger.trace("Found tunable field ${clazz.typeName}") - - for(subclass in clazz.declaredClasses) { - if(ReflectUtil.hasSuperclass(subclass, TunableFieldAcceptor::class.java)) { - tunableFieldAcceptorClasses[tunableFieldClass] = subclass as Class - logger.trace("Found acceptor for this tunable field, ${clazz.typeName}") - break - } - } - } - } - - logger.trace("Found ${tunableFieldClasses.size} tunable fields and ${tunableFieldAcceptorClasses.size} acceptors") - logger.info("Finished scanning (took ${timer.seconds()}s)") this.scanResult = ScanResult( - pipelineClasses.toTypedArray(), - tunableFieldClasses.toTypedArray(), - tunableFieldAcceptorClasses.toMap() + pipelineClasses.toTypedArray() ) return this.scanResult @@ -225,11 +193,7 @@ class ClasspathScan { /** * Result of the classpath scan * @param pipelineClasses the found OpenCvPipelines - * @param tunableFieldClasses the found TunableFields - * @param tunableFieldAcceptorClasses the found TunableFieldAcceptors */ data class ScanResult( - val pipelineClasses: Array>, - val tunableFieldClasses: Array>>, - val tunableFieldAcceptorClasses: Map>, Class> + val pipelineClasses: Array> ) \ No newline at end of file From 27aae15a7779883eae862b63a02ff8d39e64be20 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Thu, 16 Apr 2026 12:18:02 -0600 Subject: [PATCH 03/40] Improve CollapsiblePanelX by swapping label to a button --- .../gui/component/CollapsiblePanelX.kt | 78 +++++++------------ .../serivesmejia/eocvsim/gui/Visualizer.java | 6 +- 2 files changed, 32 insertions(+), 52 deletions(-) diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/component/CollapsiblePanelX.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/component/CollapsiblePanelX.kt index b7f27c3c..20a5761d 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/component/CollapsiblePanelX.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/component/CollapsiblePanelX.kt @@ -24,78 +24,58 @@ package com.github.serivesmejia.eocvsim.gui.component +import java.awt.BorderLayout import java.awt.Color +import java.awt.FlowLayout import java.awt.Font -import java.awt.event.MouseAdapter -import java.awt.event.MouseEvent import javax.swing.BorderFactory -import javax.swing.JLabel +import javax.swing.JButton import javax.swing.JPanel -import javax.swing.border.TitledBorder class CollapsiblePanelX @JvmOverloads constructor( - title: String?, + var titleText: String?, titleCol: Color?, borderCol: Color? = Color.white -) : JPanel() { - private val border: TitledBorder +) : JPanel(BorderLayout()) { private var collapsible = true var isHidden = false private set - init { - val titleAndDescriptor = if(isHidden) { - "$title (click here to expand)" - } else { - "$title (click here to hide)" + val contentPanel = JPanel() + + private val toggleButton = JButton("Hide $titleText").apply { + isFocusable = false + if (titleCol != null) foreground = titleCol + + addActionListener { + if (!collapsible) return@addActionListener + isHidden = !isHidden + contentPanel.isVisible = !isHidden + text = if (isHidden) "Show $titleText" else "Hide $titleText" + this@CollapsiblePanelX.revalidate() } + } - border = TitledBorder(titleAndDescriptor) - - border.titleColor = titleCol - border.titleFont = border.titleFont.deriveFont(Font.BOLD) - border.border = BorderFactory.createMatteBorder(1, 1, 1, 1, borderCol) - - setBorder(border) - - // as Titleborder has no access to the Label we fake the size data ;) - val l = JLabel(titleAndDescriptor) - val size = l.getPreferredSize() - - addMouseListener(object : MouseAdapter() { - override fun mouseClicked(e: MouseEvent) { - if (!collapsible) { - return - } - - val i = getBorder().getBorderInsets(this@CollapsiblePanelX) - if (e.x < i.left + size.width && e.y < i.bottom + size.height) { - - for(c in components) { - c.isVisible = !isHidden - - border.title = if(isHidden) { - "$title (click here to expand)" - } else { - "$title (click here to hide)" - } + init { + border = BorderFactory.createMatteBorder(1, 1, 1, 1, borderCol) - isHidden = !isHidden - } + val headerPanel = JPanel(FlowLayout(FlowLayout.LEFT, 5, 0)).apply { + border = BorderFactory.createEmptyBorder(2, 2, 2, 5) + add(toggleButton) + } - revalidate() - e.consume() - } - } - }) + super.add(headerPanel, BorderLayout.NORTH) + super.add(contentPanel, BorderLayout.CENTER) } fun setCollapsible(collapsible: Boolean) { this.collapsible = collapsible + toggleButton.isVisible = collapsible } fun setTitle(title: String?) { - border.title = title + titleText = title + toggleButton.text = if (isHidden) "Show $titleText" else "Hide $titleText" } } \ 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 500b205a..aab679a6 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 @@ -85,7 +85,7 @@ public class Visualizer { public OpModeSelectorPanel opModeSelectorPanel = null; - public JPanel tunerCollapsible; + public CollapsiblePanelX tunerCollapsible; private String title = "EasyOpenCV Simulator v" + Build.standardVersionString; private String titleMsg = "No pipeline"; @@ -183,14 +183,14 @@ public void init(Theme theme) { frame.getContentPane().setDropTarget(new InputSourceDropTarget(eocvSim)); tunerCollapsible = new CollapsiblePanelX("Variable Tuner", null, null); - tunerCollapsible.setLayout(new BoxLayout(tunerCollapsible, BoxLayout.LINE_AXIS)); + tunerCollapsible.getContentPanel().setLayout(new BoxLayout(tunerCollapsible.getContentPanel(), BoxLayout.LINE_AXIS)); tunerCollapsible.setVisible(false); JScrollPane tunerScrollPane = new JScrollPane(tunerMenuPanel); tunerScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); tunerScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); - tunerCollapsible.add(tunerScrollPane); + tunerCollapsible.getContentPanel().add(tunerScrollPane); onPluginGuiAttachment.run(); onPluginGuiAttachment.setCallRightAway(true); From f890d60758716dfff03058880dbbfda5ece6fa91 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Thu, 16 Apr 2026 15:56:56 -0600 Subject: [PATCH 04/40] Remove controversial witty comment in CrashReport --- .../eocvsim/input/InputSourceManager.kt | 24 ++++++++++++++----- .../eocvsim/pipeline/PipelineManager.kt | 2 +- .../eocvsim/tuner/field/NumericField.kt | 1 + .../util/exception/handling/CrashReport.kt | 5 ++-- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt index 7bc8c85b..e1f49b5f 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt @@ -40,6 +40,7 @@ import org.opencv.imgproc.Imgproc import java.awt.Dimension import java.io.File import java.io.IOException +import org.openftc.easyopencv.MatRecycler import java.util.concurrent.CancellationException import javax.swing.SwingUtilities @@ -55,6 +56,7 @@ class InputSourceManager(val eocvSim: EOCVSim) { val sources = mutableMapOf() val inputSourceLoader = InputSourceLoader() + private lateinit var matRecycler: MatRecycler val onSourcesListChange = EventHandler("InputSourceManager-OnSourcesListChange") @@ -65,6 +67,8 @@ class InputSourceManager(val eocvSim: EOCVSim) { fun init() { logger.info("Initializing...") + matRecycler = MatRecycler(4) + if (lastMatFromSource == null) { lastMatFromSource = Mat(Size(640.0, 480.0), 24) // 24 is CV_8UC4 (RGBA) lastMatFromSource!!.setTo(BLACK) @@ -111,7 +115,7 @@ class InputSourceManager(val eocvSim: EOCVSim) { logger.error("Error while creating default image input source", e) } } - + fun update(isPaused: Boolean) { val currentSource = currentInputSource ?: return @@ -121,12 +125,20 @@ class InputSourceManager(val eocvSim: EOCVSim) { val m = currentSource.update() if (m != null && !m.empty()) { - lastMatFromSource?.apply { - setTo(BLACK) // clear previous mat - m.copyTo(this) - // add an extra alpha channel because that's what eocv returns for some reason... (more realistic simulation lol) - Imgproc.cvtColor(this, this, Imgproc.COLOR_RGB2RGBA) + val nextMat = matRecycler.takeMatOrNull() ?: matRecycler.takeMatOrInterrupt() + + // Directly convert from the source 'm' (RGB) into our properly sized 'nextMat' (RGBA) + // This avoids allocating native buffers once nextMat is initialized natively for the first few frames + Imgproc.cvtColor(m, nextMat, Imgproc.COLOR_RGB2RGBA) + + val prev = lastMatFromSource + if (prev is MatRecycler.RecyclableMat) { + prev.returnMat() + } else { + prev?.release() } + + lastMatFromSource = nextMat } } catch (ex: Exception) { logger.error("Error while processing current source", ex) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt index 3900a30b..932b2e35 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt @@ -502,7 +502,7 @@ class PipelineManager( for ((instantiatorFor, instantiator) in pipelineInstantiators) { logger.debug( - " - Checking against instantiator for {} {}", + " - Checking against instantiator for {} loaded by {}", instantiatorFor.name, instantiatorFor.classLoader ) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt index 57d3e856..2ef99171 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt @@ -13,6 +13,7 @@ abstract class NumericField( allowMode: AllowMode ) : TunableField(target, reflectionField, eocvSim, allowMode) { + @Suppress("UNCHECKED_CAST") protected var _value: T? = initialFieldValue as T? protected val tunableValue by lazy { TunableNumber(_value?.toDouble() ?: 0.0, { _value?.toDouble() ?: 0.0 }, { updateNumber(it) }) } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt index f3d56485..93e3c118 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt @@ -71,9 +71,8 @@ class CrashReport(causedByException: Throwable, isDummy: Boolean = false) { "On the bright side, I bought you a teddy bear!", "Daisy, daisy...", "Oh - I know what I did wrong!", - "Hey, that tickles! Hehehe!", - "I blame Dean Kamen.", - "You should try our sister simulator!", + "I blame ESD.", + "You should try PaperVision!", "Don't be sad. I'll do better next time, I promise!", "Don't be sad, have a hug! <3", "I just don't know what went wrong :(", From 5e29bb2749d6b0d2d17e5b0ded288935c2d39691 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Thu, 16 Apr 2026 15:58:10 -0600 Subject: [PATCH 05/40] Remove philosophical quotes in CrashReport --- .../eocvsim/util/exception/handling/CrashReport.kt | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt index 93e3c118..b7b921f3 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt @@ -87,13 +87,7 @@ class CrashReport(causedByException: Throwable, isDummy: Boolean = false) { "Don't do that.", "Ouch. That hurt :(", "This is a token for 1 free hug. Redeem at your nearest local team: [~~HUG~~]", - "But it works on my machine!", - "Y a través de las estrellas ella viaja...", - "Tras el extraño silencio del sol ausente", - "no lo pienses demasiado, todo estará bien,", - "encontrarás otro desastre que hacer,", - "¿qué será de sentarse bajo la tumba del sol sin nunca hacer un desastre?", - "y por una ultima noche juntos," + "But it works on my machine!" ) @JvmStatic val defaultCrashFileName get() = "crashreport-$defaultFileName" From 3d3a7a22d74e52f96d627a0ddad033a8c62a4357 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 21 Apr 2026 11:29:06 -0600 Subject: [PATCH 06/40] Refactor Visualizer class into kotlin & removed pleaseWaitDialog --- .../github/serivesmejia/eocvsim/EOCVSim.kt | 25 +- .../eocvsim/gui/DialogFactory.java | 262 -------- .../serivesmejia/eocvsim/gui/DialogFactory.kt | 280 +++++++++ .../serivesmejia/eocvsim/gui/Visualizer.java | 558 ------------------ .../serivesmejia/eocvsim/gui/Visualizer.kt | 364 ++++++++++++ .../gui/component/input/FileSelector.kt | 2 +- .../gui/component/visualizer/TopMenuBar.kt | 2 +- .../pipeline/SourceSelectorPanel.kt | 31 +- .../eocvsim/gui/dialog/About.java | 4 - .../eocvsim/gui/dialog/FileAlreadyExists.java | 3 - .../gui/dialog/source/CreateSource.java | 4 - .../eocvsim/input/InputSourceInitializer.kt | 5 +- .../eocvsim/input/InputSourceManager.kt | 62 +- .../compiler/CompiledPipelineManager.kt | 2 +- .../eocvsim/plugin/api/impl/SwingApisImpl.kt | 10 +- .../eocvsim/tuner/TunableValue.kt | 12 +- .../eocvsim/tuner/field/NumericField.kt | 16 +- .../tuner/field/numeric/DoubleField.kt | 6 +- .../eocvsim/tuner/field/numeric/FloatField.kt | 6 +- .../tuner/field/numeric/IntegerField.kt | 6 +- .../eocvsim/tuner/field/numeric/LongField.kt | 6 +- .../eocvsim/plugin/loader/PluginManager.kt | 2 +- .../external/gui/SwingOpenCvViewport.kt | 22 +- .../main/java/org/opencv/android/Utils.java | 4 +- 24 files changed, 757 insertions(+), 937 deletions(-) delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt 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 78d5e191..f731bcb0 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 @@ -42,31 +42,28 @@ import com.github.serivesmejia.eocvsim.util.exception.handling.CrashReport import com.github.serivesmejia.eocvsim.util.exception.handling.EOCVSimUncaughtExceptionHandler import com.github.serivesmejia.eocvsim.util.fps.FpsLimiter import com.github.serivesmejia.eocvsim.util.io.EOCVSimFolder -import io.github.deltacv.common.util.loggerFor import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager 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.common.util.loggerFor +import io.github.deltacv.eocvsim.plugin.loader.PluginManager import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel -import io.github.deltacv.eocvsim.plugin.loader.PluginManager -import org.openftc.easyopencv.OpenCvViewport import nu.pattern.OpenCV import org.opencv.core.Mat import org.opencv.core.Size +import org.openftc.easyopencv.OpenCvViewport import org.openftc.easyopencv.TimestampedPipelineHandler -import android.graphics.Canvas -import java.awt.Dimension import java.io.File import java.lang.Thread.sleep import javax.swing.JOptionPane import javax.swing.SwingUtilities import javax.swing.filechooser.FileFilter import javax.swing.filechooser.FileNameExtensionFilter -import kotlin.jvm.Throws import kotlin.system.exitProcess /** @@ -97,7 +94,7 @@ class EOCVSim(val params: Parameters = Parameters()) { val logger by loggerFor(EOCVSim::class) init { - EOCVSimFolder // mkdir needed folders + EOCVSimFolder.mkdir() // mkdir needed folders } private var isNativeLibLoaded = false @@ -157,7 +154,7 @@ class EOCVSim(val params: Parameters = Parameters()) { * and the viewport where the pipeline output is shown * @see Visualizer */ - @JvmField val visualizer = Visualizer(this) + val visualizer = Visualizer(this) /** * The configuration manager instance in charge of managing @@ -343,13 +340,11 @@ class EOCVSim(val params: Parameters = Parameters()) { //shows a warning when a pipeline gets "stuck" pipelineManager.onPipelineTimeout { - visualizer.asyncPleaseWaitDialog( + DialogFactory.createInformation( + visualizer.frame, "Current pipeline took too long to ${pipelineManager.lastPipelineAction}", "Falling back to DefaultPipeline", - "Close", - Dimension(310, 150), - true, - true + "Operation failed" ) } @@ -468,7 +463,7 @@ class EOCVSim(val params: Parameters = Parameters()) { fpsLimiter.maxFPS = config.pipelineMaxFps.fps.toDouble() try { fpsLimiter.sync() - } catch (e: InterruptedException) { + } catch (_: InterruptedException) { break } } @@ -561,7 +556,7 @@ class EOCVSim(val params: Parameters = Parameters()) { logger.info("Recording session stopped") DialogFactory.createFileChooser( - visualizer.frame, DialogFactory.FileChooser.Mode.SAVE_FILE_SELECT, FileFilters.recordedVideoFilter + visualizer.frame, DialogFactory.FileChooser.Mode.SAVE_FILE_SELECT, "", FileFilters.recordedVideoFilter ).addCloseListener { _: Int, file: File?, selectedFileFilter: FileFilter? -> onMainUpdate.once { if (file != null) { 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 deleted file mode 100644 index ba605c77..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.gui; - -import com.github.serivesmejia.eocvsim.EOCVSim; -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.*; -import com.github.serivesmejia.eocvsim.input.SourceType; -import com.github.serivesmejia.eocvsim.util.event.EventHandler; -import io.github.deltacv.eocvsim.plugin.loader.PluginManager; - -import javax.swing.*; -import javax.swing.filechooser.FileFilter; -import javax.swing.filechooser.FileNameExtensionFilter; -import java.awt.*; -import java.io.File; -import java.util.ArrayList; -import java.util.function.IntConsumer; - -public class DialogFactory { - - private DialogFactory() { } - - public static void createYesOrNo(Component parent, String message, String submessage, IntConsumer result) { - JPanel panel = new JPanel(); - - JLabel label1 = new JLabel(message); - panel.add(label1); - - if (!submessage.isBlank()) { - JLabel label2 = new JLabel(submessage); - panel.add(label2); - panel.setLayout(new GridLayout(2, 1)); - } - - invokeLater(() -> result.accept( - JOptionPane.showConfirmDialog(parent, panel, "Confirm", - JOptionPane.YES_NO_OPTION, - JOptionPane.PLAIN_MESSAGE - ) - )); - } - - public static FileChooser createFileChooser(Component parent, FileChooser.Mode mode, FileFilter... filters) { - FileChooser fileChooser = new FileChooser(parent, mode, "", filters); - invokeLater(fileChooser::init); - return fileChooser; - } - - public static FileChooser createFileChooser(Component parent, FileChooser.Mode mode, String initialFileName, FileFilter... filters) { - FileChooser fileChooser = new FileChooser(parent, mode, initialFileName, filters); - invokeLater(fileChooser::init); - return fileChooser; - } - - public static FileChooser createFileChooser(Component parent, FileFilter... filters) { - return createFileChooser(parent, null, "", filters); - } - - public static FileChooser createFileChooser(Component parent, FileChooser.Mode mode) { - return createFileChooser(parent, mode, new FileFilter[0]); - } - - public static FileChooser createFileChooser(Component parent) { - return createFileChooser(parent, null, new FileFilter[0]); - } - - public static void createSourceDialog(EOCVSim eocvSim, - SourceType type, - File initialFile) { - invokeLater(() -> { - switch (type) { - case IMAGE: - new CreateImageSource(eocvSim.visualizer.frame, eocvSim, initialFile); - break; - case CAMERA: - new CreateCameraSource(eocvSim.visualizer.frame, eocvSim); - break; - case VIDEO: - new CreateVideoSource(eocvSim.visualizer.frame, eocvSim, initialFile); - break; - case HTTP: - new CreateHttpSource(eocvSim.visualizer.frame, eocvSim); - break; - } - }); - } - - public static void createSourceDialog(EOCVSim eocvSim, SourceType type) { - createSourceDialog(eocvSim, type, null); - } - - public static void createSourceDialog(EOCVSim eocvSim) { - invokeLater(() -> new CreateSource(eocvSim.visualizer.frame, eocvSim)); - } - - public static void createSourceExDialog(EOCVSim eocvSim) { - invokeLater(() -> new CreateSourceEx(eocvSim.visualizer.frame, eocvSim.visualizer)); - } - - public static void createConfigDialog(EOCVSim eocvSim) { - invokeLater(() -> new Configuration(eocvSim.visualizer.frame, eocvSim)); - } - - public static void createAboutDialog(EOCVSim eocvSim) { - invokeLater(() -> new About(eocvSim.visualizer.frame, eocvSim)); - } - - public static void createOutput(EOCVSim eocvSim, boolean wasManuallyOpened) { - invokeLater(() -> { - if(!Output.Companion.isAlreadyOpened()) - new Output(eocvSim.visualizer.frame, eocvSim, Output.Companion.getLatestIndex(), wasManuallyOpened); - }); - } - - public static void createOutput(EOCVSim eocvSim) { - createOutput(eocvSim, false); - } - - public static void createBuildOutput(EOCVSim eocvSim) { - invokeLater(() -> { - if(!Output.Companion.isAlreadyOpened()) - new Output(eocvSim.visualizer.frame, eocvSim, 1); - }); - } - - public static void createPipelineOutput(EOCVSim eocvSim) { - invokeLater(() -> { - if(!Output.Companion.isAlreadyOpened()) - new Output(eocvSim.visualizer.frame, eocvSim, 0); - }); - } - - public static AppendDelegate createMavenOutput(PluginManager manager, Runnable onContinue) { - AppendDelegate delegate = new AppendDelegate(); - - invokeLater(() -> new PluginOutput(delegate, manager, manager.getEocvSim(), onContinue)); - - return delegate; - } - - 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 void createCrashReport(Visualizer visualizer, String crash) { - invokeLater(() -> new CrashReportOutput(visualizer == null ? null : visualizer.frame, crash)); - } - - public static FileAlreadyExists.UserChoice createFileAlreadyExistsDialog(EOCVSim eocvSim) { - return new FileAlreadyExists(eocvSim.visualizer.frame, eocvSim).run(); - } - - private static void invokeLater(Runnable runn) { - SwingUtilities.invokeLater(runn); - } - - public static class FileChooser { - private final JFileChooser chooser; - private final Component parent; - - private final Mode mode; - - private final ArrayList closeListeners = new ArrayList<>(); - - public FileChooser(Component parent, Mode mode, String initialFileName, FileFilter... filters) { - if (mode == null) mode = Mode.FILE_SELECT; - - chooser = new JFileChooser(); - - this.parent = parent; - this.mode = mode; - - if (mode == Mode.DIRECTORY_SELECT) { - chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - // disable the "All files" option. - chooser.setAcceptAllFileFilterUsed(false); - } - - chooser.setSelectedFile(new File(chooser.getSelectedFile(), initialFileName)); - - if (filters != null) { - for (FileFilter filter : filters) { - if(filter != null) chooser.addChoosableFileFilter(filter); - } - if(filters.length > 0) { - chooser.setFileFilter(filters[0]); - } - } - } - - protected void init() { - int returnVal; - - if (mode == Mode.SAVE_FILE_SELECT) { - returnVal = chooser.showSaveDialog(parent); - } else { - returnVal = chooser.showOpenDialog(parent); - } - - executeCloseListeners(returnVal, chooser.getSelectedFile(), chooser.getFileFilter()); - } - - public void addCloseListener(FileChooserCloseListener listener) { - this.closeListeners.add(listener); - } - - private void executeCloseListeners(int OPTION, File selectedFile, FileFilter selectedFileFilter) { - for (FileChooserCloseListener listener : closeListeners) { - listener.onClose(OPTION, selectedFile, selectedFileFilter); - } - } - - public void close() { - chooser.setVisible(false); - executeCloseListeners(JFileChooser.CANCEL_OPTION, new File(""), new FileNameExtensionFilter("", "")); - } - - public enum Mode { FILE_SELECT, DIRECTORY_SELECT, SAVE_FILE_SELECT } - - public interface FileChooserCloseListener { - void onClose(int OPTION, File selectedFile, FileFilter selectedFileFilter); - } - } - -} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt new file mode 100644 index 00000000..39e75c3b --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2021 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.gui + +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.gui.dialog.* +import com.github.serivesmejia.eocvsim.gui.dialog.SplashScreen // Explicit import to resolve reference +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.* +import com.github.serivesmejia.eocvsim.input.SourceType +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import io.github.deltacv.eocvsim.plugin.loader.PluginManager +import java.awt.* +import java.io.File +import java.util.* +import javax.swing.* +import javax.swing.filechooser.FileFilter +import javax.swing.filechooser.FileNameExtensionFilter + +object DialogFactory { + + @JvmStatic + fun createYesOrNo(parent: Component?, message: String, submessage: String, result: (Int) -> Unit) { + val panel = JPanel() + val label1 = JLabel(message) + panel.add(label1) + + if (submessage.isNotBlank()) { + val label2 = JLabel(submessage) + panel.add(label2) + panel.layout = GridLayout(2, 1) + } + + invokeLater { + result( + JOptionPane.showConfirmDialog( + parent, panel, "Confirm", + JOptionPane.YES_NO_OPTION, + JOptionPane.PLAIN_MESSAGE + ) + ) + } + } + + @JvmStatic + @JvmOverloads + fun createInformation( + parent: Component?, + message: String, + submessage: String? = null, + title: String = "Information", + cancelText: String? = null, + onCancel: (() -> Unit)? = null + ): JDialog { + val panel = JPanel().apply { + layout = GridLayout(if (submessage != null) 2 else 1, 1) + add(JLabel(message)) + if (submessage != null) add(JLabel(submessage)) + } + + val options = if (cancelText != null) arrayOf(cancelText) else emptyArray() + val pane = JOptionPane(panel, JOptionPane.INFORMATION_MESSAGE, JOptionPane.DEFAULT_OPTION, null, options) + val dialog = pane.createDialog(parent, title) + + if (cancelText != null) { + pane.addPropertyChangeListener { evt -> + if (evt.propertyName == JOptionPane.VALUE_PROPERTY) { + if (pane.value == cancelText) { + onCancel?.invoke() + } + } + } + } + + dialog.isModal = false + + invokeLater { dialog.isVisible = true } + + return dialog + } + + @JvmStatic + fun createFileChooser( + parent: Component?, + mode: FileChooser.Mode?, + initialFileName: String, + vararg filters: FileFilter? + ): FileChooser { + val fileChooser = FileChooser(parent, mode, initialFileName, *filters) + invokeLater { fileChooser.init() } + return fileChooser + } + + @JvmStatic + fun createFileChooser( + parent: Component?, + mode: FileChooser.Mode?, + vararg filters: FileFilter? + ): FileChooser = createFileChooser(parent, mode, "", *filters) + + @JvmStatic + fun createFileChooser(parent: Component?, vararg filters: FileFilter?): FileChooser = + createFileChooser(parent, null, "", *filters) + + @JvmStatic + @JvmOverloads + fun createSourceDialog(eocvSim: EOCVSim, type: SourceType, initialFile: File? = null) { + invokeLater { + when (type) { + SourceType.IMAGE -> CreateImageSource(eocvSim.visualizer.frame, eocvSim, initialFile) + SourceType.CAMERA -> CreateCameraSource(eocvSim.visualizer.frame, eocvSim) + SourceType.VIDEO -> CreateVideoSource(eocvSim.visualizer.frame, eocvSim, initialFile) + SourceType.HTTP -> CreateHttpSource(eocvSim.visualizer.frame, eocvSim) + else -> {} + } + } + } + + @JvmStatic + fun createSourceExDialog(eocvSim: EOCVSim) { + invokeLater { CreateSourceEx(eocvSim.visualizer.frame, eocvSim.visualizer) } + } + + @JvmStatic + fun createConfigDialog(eocvSim: EOCVSim) { + invokeLater { Configuration(eocvSim.visualizer.frame, eocvSim) } + } + + @JvmStatic + fun createAboutDialog(eocvSim: EOCVSim) { + invokeLater { About(eocvSim.visualizer.frame, eocvSim) } + } + + @JvmStatic + @JvmOverloads + fun createOutput(eocvSim: EOCVSim, wasManuallyOpened: Boolean = false) { + invokeLater { + if (!Output.isAlreadyOpened) { + Output(eocvSim.visualizer.frame, eocvSim, Output.latestIndex, wasManuallyOpened) + } + } + } + + @JvmStatic + fun createBuildOutput(eocvSim: EOCVSim) { + invokeLater { + if (!Output.isAlreadyOpened) { + Output(eocvSim.visualizer.frame, eocvSim, 1) + } + } + } + + @JvmStatic + fun createPipelineOutput(eocvSim: EOCVSim) { + invokeLater { + if (!Output.isAlreadyOpened) { + Output(eocvSim.visualizer.frame, eocvSim, 0) + } + } + } + + @JvmStatic + fun createMavenOutput(manager: PluginManager, onContinue: Runnable?): AppendDelegate { + val delegate = AppendDelegate() + invokeLater { PluginOutput(delegate, manager, manager.eocvSim, onContinue ?: Runnable { }) } + return delegate + } + + @JvmStatic + fun createSplashScreen(closeHandler: EventHandler?) { + invokeLater { SplashScreen(closeHandler) } + } + + @JvmStatic + fun createIAmA(visualizer: Visualizer) { + invokeLater { IAmA(visualizer.frame, visualizer) } + } + + @JvmStatic + fun createIAmAPaperVision(visualizer: Visualizer, showWorkspacesButton: Boolean) { + invokeLater { IAmAPaperVision(visualizer.frame, visualizer, false, showWorkspacesButton) } + } + + @JvmStatic + fun createWorkspace(visualizer: Visualizer) { + invokeLater { CreateWorkspace(visualizer.frame, visualizer) } + } + + @JvmStatic + fun createCrashReport(visualizer: Visualizer?, crash: String?) { + invokeLater { CrashReportOutput(visualizer?.frame, crash ?: "") } + } + + @JvmStatic + fun createFileAlreadyExistsDialog(eocvSim: EOCVSim): FileAlreadyExists.UserChoice { + return FileAlreadyExists(eocvSim.visualizer.frame, eocvSim).run() + } + + private fun invokeLater(runn: Runnable) { + SwingUtilities.invokeLater(runn) + } + + class FileChooser @JvmOverloads constructor( + private val parent: Component?, + val mode: Mode? = null, + initialFileName: String = "", + vararg filters: FileFilter? + ) { + private val chooser: JFileChooser = JFileChooser() + private val closeListeners = ArrayList() + + init { + val finalMode = mode ?: Mode.FILE_SELECT + if (finalMode == Mode.DIRECTORY_SELECT) { + chooser.fileSelectionMode = JFileChooser.DIRECTORIES_ONLY + chooser.isAcceptAllFileFilterUsed = false + } + + chooser.selectedFile = File(chooser.selectedFile, initialFileName) + + filters.filterNotNull().forEach { chooser.addChoosableFileFilter(it) } + if (filters.isNotEmpty()) { + chooser.fileFilter = filters[0] + } + } + + fun init() { + val returnVal = if (mode == Mode.SAVE_FILE_SELECT) { + chooser.showSaveDialog(parent) + } else { + chooser.showOpenDialog(parent) + } + executeCloseListeners(returnVal, chooser.selectedFile, chooser.fileFilter) + } + + fun addCloseListener(listener: FileChooserCloseListener): FileChooser { + closeListeners.add(listener) + return this + } + + private fun executeCloseListeners(OPTION: Int, selectedFile: File?, selectedFileFilter: FileFilter?) { + for (listener in closeListeners) { + listener.onClose(OPTION, selectedFile, selectedFileFilter) + } + } + + fun close() { + chooser.isVisible = false + executeCloseListeners(JFileChooser.CANCEL_OPTION, File(""), FileNameExtensionFilter("", "")) + } + + enum class Mode { FILE_SELECT, DIRECTORY_SELECT, SAVE_FILE_SELECT } + + fun interface FileChooserCloseListener { + fun onClose(OPTION: Int, selectedFile: File?, selectedFileFilter: FileFilter?) + } + } +} 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 deleted file mode 100644 index aab679a6..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.java +++ /dev/null @@ -1,558 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.gui; - -import com.formdev.flatlaf.FlatLaf; -import kotlinx.coroutines.CoroutineScope; -import com.github.serivesmejia.eocvsim.Build; -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.gui.component.CollapsiblePanelX; -import com.github.serivesmejia.eocvsim.gui.component.visualizer.*; -import com.github.serivesmejia.eocvsim.gui.component.visualizer.opmode.OpModeSelectorPanel; -import com.github.serivesmejia.eocvsim.gui.component.visualizer.opmode.SidebarOpModeTabPanel; -import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SidebarPipelineTabPanel; -import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SourceSelectorPanel; -import io.github.deltacv.vision.external.gui.SwingOpenCvViewport; -import com.github.serivesmejia.eocvsim.gui.component.tuner.ColorPicker; -import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel; -import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.PipelineSelectorPanel; -import com.github.serivesmejia.eocvsim.gui.theme.Theme; -import com.github.serivesmejia.eocvsim.pipeline.compiler.PipelineCompiler; -import com.github.serivesmejia.eocvsim.util.event.EventHandler; -import com.github.serivesmejia.eocvsim.workspace.util.VSCodeLauncher; -import com.github.serivesmejia.eocvsim.workspace.util.template.GradleWorkspaceTemplate; -import kotlin.Unit; -import org.opencv.core.Size; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import java.awt.*; -import java.awt.event.*; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -public class Visualizer { - - public final EventHandler onInitFinished = new EventHandler("OnVisualizerInitFinish"); - public final EventHandler onPluginGuiAttachment = new EventHandler("OnPluginGuiAttachment"); - - public final ArrayList pleaseWaitDialogs = new ArrayList<>(); - - public final ArrayList childFrames = new ArrayList<>(); - public final ArrayList childDialogs = new ArrayList<>(); - - public final EOCVSim eocvSim; - public JFrame frame; - - public SwingOpenCvViewport viewport = null; - - public TopMenuBar menuBar = null; - public JPanel tunerMenuPanel; - - public JPanel sidebarContainer = null; - - public SidebarPanel sidebarPanel = null; - - public SidebarPipelineTabPanel sidebarPipelineTabPanel= null; - public SidebarOpModeTabPanel sidebarOpModeTabPanel= null; - - public PipelineSelectorPanel pipelineSelectorPanel = null; - public SourceSelectorPanel sourceSelectorPanel = null; - - public OpModeSelectorPanel opModeSelectorPanel = null; - - public CollapsiblePanelX tunerCollapsible; - - private String title = "EasyOpenCV Simulator v" + Build.standardVersionString; - private String titleMsg = "No pipeline"; - private String beforeTitle = ""; - private String beforeTitleMsg = ""; - - public ColorPicker colorPicker = null; - - private volatile boolean hasFinishedInitializing = false; - - Logger logger = LoggerFactory.getLogger(getClass()); - - public Visualizer(EOCVSim eocvSim) { - this.eocvSim = eocvSim; - } - - 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(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) { - logger.error("Security exception while setting TaskBar icon", e); - } - } - - try { - theme.install(); - } catch (Exception e) { - logger.error("Failed to install theme {}", theme.name(), e); - } - - Icons.INSTANCE.setDark(FlatLaf.isLafDark()); - - if (Build.isDev) { - title += "-dev "; - } - - //instantiate all swing elements after theme installation - frame = new JFrame(); - - String fpsMeterDescriptor = "deltacv EOCV-Sim v" + Build.standardVersionString; - if (Build.isDev) fpsMeterDescriptor += "-dev"; - - viewport = new SwingOpenCvViewport(new Size(1080, 720), fpsMeterDescriptor); - viewport.setDark(FlatLaf.isLafDark()); - - colorPicker = new ColorPicker(viewport); - - JLayeredPane skiaPanel = viewport.skiaPanel(); - skiaPanel.setLayout(new BorderLayout()); - - frame.add(skiaPanel); - - menuBar = new TopMenuBar(this, eocvSim); - - tunerMenuPanel = new JPanel(); - - sidebarPanel = new SidebarPanel(eocvSim); - - sidebarPipelineTabPanel = new SidebarPipelineTabPanel(eocvSim); - pipelineSelectorPanel = sidebarPipelineTabPanel.getPipelineSelectorPanel(); - sourceSelectorPanel = sidebarPipelineTabPanel.getSourceSelectorPanel(); - - sidebarOpModeTabPanel = new SidebarOpModeTabPanel(eocvSim); - opModeSelectorPanel = sidebarOpModeTabPanel.getOpModeSelectorPanel(); - - sidebarPanel.add("Pipeline", sidebarPipelineTabPanel); - sidebarPanel.add("OpMode", sidebarOpModeTabPanel); - - sidebarContainer = new JPanel(); - - /* - * TOP MENU BAR - */ - - frame.setJMenuBar(menuBar); - - /* - * IMG VISUALIZER & SCROLL PANE - */ - - sidebarContainer.setLayout(new BoxLayout(sidebarContainer, BoxLayout.Y_AXIS)); - // add pretty border - sidebarContainer.setBorder( - BorderFactory.createMatteBorder(0, 1, 0, 0, UIManager.getColor("Separator.foreground")) - ); - - sidebarPanel.setBorder(new EmptyBorder(0, 0, 0, 0)); - sidebarContainer.add(sidebarPanel); - - //global - frame.getContentPane().setDropTarget(new InputSourceDropTarget(eocvSim)); - - tunerCollapsible = new CollapsiblePanelX("Variable Tuner", null, null); - tunerCollapsible.getContentPanel().setLayout(new BoxLayout(tunerCollapsible.getContentPanel(), BoxLayout.LINE_AXIS)); - tunerCollapsible.setVisible(false); - - JScrollPane tunerScrollPane = new JScrollPane(tunerMenuPanel); - tunerScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); - tunerScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); - - tunerCollapsible.getContentPanel().add(tunerScrollPane); - - onPluginGuiAttachment.run(); - onPluginGuiAttachment.setCallRightAway(true); - - frame.add(tunerCollapsible, BorderLayout.SOUTH); - frame.add(sidebarContainer, BorderLayout.EAST); - - //initialize other various stuff of the frame - frame.setSize(780, 645); - frame.setMinimumSize(frame.getSize()); - frame.setTitle("EasyOpenCV Simulator - No Pipeline"); - - frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); - - 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); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - frame.setVisible(true); - - onInitFinished.run(); - onInitFinished.setCallRightAway(true); - - registerListeners(); - - hasFinishedInitializing = true; - - if (!PipelineCompiler.Companion.getIS_USABLE()) { - compilerUnsupported(); - } - } - - public void initAsync(Theme simTheme) { - SwingUtilities.invokeLater(() -> init(simTheme)); - } - - private void registerListeners() { - - frame.addWindowListener(new WindowAdapter() { - public void windowClosing(WindowEvent e) { - eocvSim.onMainUpdate.once(eocvSim::destroy); - } - }); - - //handling onViewportTapped evts - viewport.getComponent().addMouseListener(new MouseAdapter() { - public void mouseClicked(MouseEvent e) { - if (!colorPicker.isPicking()) - eocvSim.pipelineManager.callViewportTapped(); - } - }); - - // stop color-picking mode when changing pipeline - eocvSim.pipelineManager.onPipelineChange.attach(() -> colorPicker.stopPicking()); - } - - public boolean hasFinishedInit() { - return hasFinishedInitializing; - } - - public void joinInit() { - while (!hasFinishedInitializing) { - Thread.onSpinWait(); - } - } - - public void close() { - SwingUtilities.invokeLater(() -> { - frame.setVisible(false); - viewport.deactivate(); - - //close all asyncpleasewait dialogs - for (AsyncPleaseWaitDialog dialog : pleaseWaitDialogs) { - if (dialog != null) { - dialog.destroyDialog(); - } - } - - pleaseWaitDialogs.clear(); - - //close all opened frames - for (JFrame frame : childFrames) { - if (frame != null && frame.isVisible()) { - frame.setVisible(false); - frame.dispose(); - } - } - - childFrames.clear(); - - //close all opened dialogs - for (JDialog dialog : childDialogs) { - if (dialog != null && dialog.isVisible()) { - dialog.setVisible(false); - dialog.dispose(); - } - } - - childDialogs.clear(); - frame.dispose(); - viewport.deactivate(); - }); - } - - private void setFrameTitle(String title, String titleMsg) { - frame.setTitle(title + " - " + titleMsg); - } - - public void setTitle(String title) { - this.title = title; - if (!beforeTitle.equals(title)) setFrameTitle(title, titleMsg); - beforeTitle = title; - } - - public void setTitleMessage(String titleMsg) { - this.titleMsg = titleMsg; - if (!beforeTitleMsg.equals(title)) setFrameTitle(title, titleMsg); - beforeTitleMsg = titleMsg; - } - - public void updateTunerFields(List fields) { - tunerMenuPanel.removeAll(); - tunerMenuPanel.repaint(); - - for (TunableFieldPanel fieldPanel : fields) { - tunerMenuPanel.add(fieldPanel); - fieldPanel.showFieldPanel(); - } - - tunerCollapsible.setVisible(!fields.isEmpty()); - } - - - public void compilerUnsupported() { - asyncPleaseWaitDialog( - "Runtime pipeline builds are not supported on this JVM", - "For further info, check the EOCV-Sim docs", - "Close", - new Dimension(320, 160), - true, true - ); - } - - public void selectPipelinesWorkspace() { - DialogFactory.createFileChooser( - frame, DialogFactory.FileChooser.Mode.DIRECTORY_SELECT - ).addCloseListener((OPTION, selectedFile, selectedFileFilter) -> { - if (OPTION == JFileChooser.APPROVE_OPTION) { - if (!selectedFile.exists()) selectedFile.mkdir(); - - eocvSim.onMainUpdate.once(() -> - eocvSim.workspaceManager.setWorkspaceFile(selectedFile) - ); - } - }); - } - - public void createVSCodeWorkspace() { - DialogFactory.createFileChooser(frame, DialogFactory.FileChooser.Mode.DIRECTORY_SELECT) - .addCloseListener((OPTION, selectedFile, selectedFileFilter) -> { - if (OPTION == JFileChooser.APPROVE_OPTION) { - if (!selectedFile.exists()) selectedFile.mkdir(); - - if (selectedFile.isDirectory() && Objects.requireNonNull(selectedFile.listFiles()).length == 0) { - eocvSim.workspaceManager.createWorkspaceWithTemplateAsync( - selectedFile, GradleWorkspaceTemplate.INSTANCE, - () -> { - askOpenVSCode(); - return Unit.INSTANCE; // weird kotlin interop - } - ); - } else { - asyncPleaseWaitDialog( - "The selected directory must be empty", "Select an empty directory or create a new one", - "Retry", new Dimension(320, 160), true, true - ).onCancel(this::createVSCodeWorkspace); - } - } - }); - } - - 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(), eocvSim.scope); - } - } - ); - } - - // PLEASE WAIT DIALOGS - - public boolean pleaseWaitDialog(JDialog diag, String message, String subMessage, String cancelBttText, Dimension size, boolean cancellable, AsyncPleaseWaitDialog apwd, boolean isError) { - final JDialog dialog = diag == null ? new JDialog(this.frame) : diag; - - boolean addSubMessage = subMessage != null; - - int rows = 3; - if (!addSubMessage) { - rows--; - } - - dialog.setModal(false); - dialog.setAlwaysOnTop(true); - dialog.setLayout(new GridLayout(rows, 1)); - - if (isError) { - dialog.setTitle("Operation failed"); - } else { - dialog.setTitle("Operation in progress"); - } - - JLabel msg = new JLabel(message); - msg.setHorizontalAlignment(JLabel.CENTER); - msg.setVerticalAlignment(JLabel.CENTER); - - dialog.add(msg); - - JLabel subMsg = null; - if (addSubMessage) { - subMsg = new JLabel(subMessage); - subMsg.setHorizontalAlignment(JLabel.CENTER); - subMsg.setVerticalAlignment(JLabel.CENTER); - - dialog.add(subMsg); - } - - JPanel exitBttPanel = new JPanel(new FlowLayout()); - JButton cancelBtt = new JButton(cancelBttText); - - cancelBtt.setEnabled(cancellable); - - exitBttPanel.add(cancelBtt); - - boolean[] cancelled = {false}; - - cancelBtt.addActionListener(e -> { - cancelled[0] = true; - dialog.setVisible(false); - dialog.dispose(); - }); - - dialog.add(exitBttPanel); - - if (apwd != null) { - apwd.msg = msg; - apwd.subMsg = subMsg; - apwd.cancelBtt = cancelBtt; - } - - if (size == null) size = new Dimension(400, 200); - dialog.setSize(size); - - dialog.setLocationRelativeTo(null); - dialog.setResizable(false); - dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); - - dialog.setVisible(true); - - return cancelled[0]; - } - - public void pleaseWaitDialog(JDialog dialog, String message, String subMessage, String cancelBttText, Dimension size, boolean cancellable) { - pleaseWaitDialog(dialog, message, subMessage, cancelBttText, size, cancellable, null, false); - } - - public void pleaseWaitDialog(String message, String subMessage, String cancelBttText, Dimension size, boolean cancellable) { - pleaseWaitDialog(null, message, subMessage, cancelBttText, size, cancellable, null, false); - } - - public AsyncPleaseWaitDialog asyncPleaseWaitDialog(String message, String subMessage, String cancelBttText, Dimension size, boolean cancellable, boolean isError) { - AsyncPleaseWaitDialog rPWD = new AsyncPleaseWaitDialog(message, subMessage, cancelBttText, size, cancellable, isError, eocvSim); - SwingUtilities.invokeLater(rPWD); - - return rPWD; - } - - public AsyncPleaseWaitDialog asyncPleaseWaitDialog(String message, String subMessage, String cancelBttText, Dimension size, boolean cancellable) { - AsyncPleaseWaitDialog rPWD = new AsyncPleaseWaitDialog(message, subMessage, cancelBttText, size, cancellable, false, eocvSim); - SwingUtilities.invokeLater(rPWD); - - return rPWD; - } - - public class AsyncPleaseWaitDialog implements Runnable { - - public volatile JDialog dialog = new JDialog(frame); - - public volatile JLabel msg = null; - public volatile JLabel subMsg = null; - - public volatile JButton cancelBtt = null; - - public volatile boolean wasCancelled = false; - public volatile boolean isError; - - public volatile String initialMessage; - public volatile String initialSubMessage; - - public volatile boolean isDestroyed = false; - - String message; - String subMessage; - String cancelBttText; - - Dimension size; - - boolean cancellable; - - private final ArrayList onCancelRunnables = new ArrayList<>(); - - public AsyncPleaseWaitDialog(String message, String subMessage, String cancelBttText, Dimension size, boolean cancellable, boolean isError, EOCVSim eocvSim) { - this.message = message; - this.subMessage = subMessage; - this.initialMessage = message; - this.initialSubMessage = subMessage; - this.cancelBttText = cancelBttText; - - this.size = size; - this.cancellable = cancellable; - - this.isError = isError; - - eocvSim.visualizer.pleaseWaitDialogs.add(this); - } - - public void onCancel(Runnable runn) { - onCancelRunnables.add(runn); - } - - @Override - public void run() { - wasCancelled = pleaseWaitDialog(dialog, message, subMessage, cancelBttText, size, cancellable, this, isError); - - if (wasCancelled) { - for (Runnable runn : onCancelRunnables) { - runn.run(); - } - } - } - - public void destroyDialog() { - if (!isDestroyed) { - dialog.setVisible(false); - dialog.dispose(); - isDestroyed = true; - } - } - - } - -} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt new file mode 100644 index 00000000..f93db9b5 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2021 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.gui + +import com.formdev.flatlaf.FlatLaf +import com.github.serivesmejia.eocvsim.Build +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.gui.component.CollapsiblePanelX +import com.github.serivesmejia.eocvsim.gui.component.tuner.ColorPicker +import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel +import com.github.serivesmejia.eocvsim.gui.component.visualizer.InputSourceDropTarget +import com.github.serivesmejia.eocvsim.gui.component.visualizer.SidebarPanel +import com.github.serivesmejia.eocvsim.gui.component.visualizer.TopMenuBar +import com.github.serivesmejia.eocvsim.gui.component.visualizer.opmode.OpModeSelectorPanel +import com.github.serivesmejia.eocvsim.gui.component.visualizer.opmode.SidebarOpModeTabPanel +import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.PipelineSelectorPanel +import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SidebarPipelineTabPanel +import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SourceSelectorPanel +import com.github.serivesmejia.eocvsim.gui.theme.Theme +import com.github.serivesmejia.eocvsim.pipeline.compiler.PipelineCompiler +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.workspace.util.VSCodeLauncher +import com.github.serivesmejia.eocvsim.workspace.util.template.GradleWorkspaceTemplate +import io.github.deltacv.common.util.loggerForThis +import io.github.deltacv.vision.external.gui.SwingOpenCvViewport +import org.opencv.core.Size +import java.awt.BorderLayout +import java.awt.Dimension +import java.awt.Taskbar +import java.awt.event.MouseAdapter +import java.awt.event.MouseEvent +import java.awt.event.WindowAdapter +import java.awt.event.WindowEvent +import javax.swing.* + +class Visualizer(val eocvSim: EOCVSim) { + + val onInitFinished = EventHandler("OnVisualizerInitFinish") + val onPluginGuiAttachment = EventHandler("OnPluginGuiAttachment") + + val childFrames = ArrayList() + val childDialogs = ArrayList() + + lateinit var frame: JFrame + private set + + private val fpsMeterDescriptor: String + get() = "deltacv EOCV-Sim v" + Build.standardVersionString + if (Build.isDev) "-dev" else "" + + @JvmField + val viewport = SwingOpenCvViewport(Size(1080.0, 720.0), fpsMeterDescriptor) + + lateinit var menuBar: TopMenuBar + private set + + lateinit var tunerMenuPanel: JPanel + private set + + lateinit var sidebarContainer: JPanel + private set + + lateinit var sidebarPanel: SidebarPanel + private set + + lateinit var sidebarPipelineTabPanel: SidebarPipelineTabPanel + private set + lateinit var sidebarOpModeTabPanel: SidebarOpModeTabPanel + private set + + lateinit var pipelineSelectorPanel: PipelineSelectorPanel + private set + lateinit var sourceSelectorPanel: SourceSelectorPanel + private set + + lateinit var opModeSelectorPanel: OpModeSelectorPanel + private set + + lateinit var tunerCollapsible: CollapsiblePanelX + private set + + private var title = "EasyOpenCV Simulator v" + Build.standardVersionString + private var titleMsg = "No pipeline" + private var beforeTitle = "" + private var beforeTitleMsg = "" + + lateinit var colorPicker: ColorPicker + private set + + @Volatile + var hasFinishedInitializing = false + private set + + private val logger by loggerForThis() + + fun init(theme: Theme) { + if (Taskbar.isTaskbarSupported()) { + try { + Taskbar.getTaskbar().iconImage = EOCVSimIconLibrary.icoEOCVSim128.image + } catch (_: UnsupportedOperationException) { + logger.warn("Setting the Taskbar icon image is not supported on this platform") + } catch (e: SecurityException) { + logger.error("Security exception while setting TaskBar icon", e) + } + } + + try { + theme.install() + } catch (e: Exception) { + logger.error("Failed to install theme ${theme.name}", e) + } + + Icons.setDark(FlatLaf.isLafDark()) + + if (Build.isDev) { + title += "-dev " + } + + frame = JFrame() + + viewport.init() + viewport.dark = FlatLaf.isLafDark() + + colorPicker = ColorPicker(viewport) + + val skiaPanel = viewport.skiaPanel() + skiaPanel.layout = BorderLayout() + + frame.add(skiaPanel) + + menuBar = TopMenuBar(this, eocvSim) + tunerMenuPanel = JPanel() + + sidebarPanel = SidebarPanel(eocvSim) + + sidebarPipelineTabPanel = SidebarPipelineTabPanel(eocvSim) + pipelineSelectorPanel = sidebarPipelineTabPanel.pipelineSelectorPanel + sourceSelectorPanel = sidebarPipelineTabPanel.sourceSelectorPanel + + sidebarOpModeTabPanel = SidebarOpModeTabPanel(eocvSim) + opModeSelectorPanel = sidebarOpModeTabPanel.opModeSelectorPanel + + sidebarPanel.add("Pipeline", sidebarPipelineTabPanel) + sidebarPanel.add("OpMode", sidebarOpModeTabPanel) + + sidebarContainer = JPanel().apply { + layout = BoxLayout(this, BoxLayout.Y_AXIS) + border = BorderFactory.createMatteBorder(0, 1, 0, 0, UIManager.getColor("Separator.foreground")) + add(sidebarPanel) + } + + frame.jMenuBar = menuBar + + frame.contentPane.dropTarget = InputSourceDropTarget(eocvSim) + + tunerCollapsible = CollapsiblePanelX("Variable Tuner", null, null).apply { + contentPanel.layout = BoxLayout(contentPanel, BoxLayout.LINE_AXIS) + isVisible = false + } + + val tunerScrollPane = JScrollPane(tunerMenuPanel).apply { + horizontalScrollBarPolicy = JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS + verticalScrollBarPolicy = JScrollPane.VERTICAL_SCROLLBAR_NEVER + } + + tunerCollapsible.contentPanel.add(tunerScrollPane) + + onPluginGuiAttachment.run() + onPluginGuiAttachment.callRightAway = true + + frame.add(tunerCollapsible, BorderLayout.SOUTH) + frame.add(sidebarContainer, BorderLayout.EAST) + + frame.size = Dimension(780, 645) + frame.minimumSize = frame.size + frame.title = "EasyOpenCV Simulator - No Pipeline" + + frame.defaultCloseOperation = JFrame.DO_NOTHING_ON_CLOSE + + frame.iconImages = listOf( + EOCVSimIconLibrary.icoEOCVSim128.image, + EOCVSimIconLibrary.icoEOCVSim64.image, + EOCVSimIconLibrary.icoEOCVSim32.image, + EOCVSimIconLibrary.icoEOCVSim16.image + ) + + frame.setLocationRelativeTo(null) + frame.extendedState = JFrame.MAXIMIZED_BOTH + frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE + + frame.isVisible = true + + onInitFinished.run() + onInitFinished.callRightAway = true + + registerListeners() + + eocvSim.inputSourceManager.onInputSourceInitError { + DialogFactory.createInformation( + frame, + "Error while loading requested source", "Falling back to previous source", + "Operation failed" + ) + } + + hasFinishedInitializing = true + + if (!PipelineCompiler.IS_USABLE) { + compilerUnsupported() + } + } + + fun initAsync(simTheme: Theme) { + SwingUtilities.invokeLater { init(simTheme) } + } + + private fun registerListeners() { + frame.addWindowListener(object : WindowAdapter() { + override fun windowClosing(e: WindowEvent) { + eocvSim.onMainUpdate.once { eocvSim.destroy() } + } + }) + + viewport.component.addMouseListener(object : MouseAdapter() { + override fun mouseClicked(e: MouseEvent) { + if (!colorPicker.isPicking) { + eocvSim.pipelineManager.callViewportTapped() + } + } + }) + + eocvSim.pipelineManager.onPipelineChange.attach { colorPicker.stopPicking() } + } + + fun joinInit() { + while (!hasFinishedInitializing) { + Thread.onSpinWait() + } + } + + fun close() { + SwingUtilities.invokeLater { + frame.isVisible = false + viewport.deactivate() + + for (child in childFrames) { + child.isVisible = false + child.dispose() + } + childFrames.clear() + + for (dialog in childDialogs) { + dialog.isVisible = false + dialog.dispose() + } + childDialogs.clear() + + frame.dispose() + } + } + + private fun updateFrameTitle(title: String, titleMsg: String) { + frame.title = "$title - $titleMsg" + } + + fun setTitle(title: String) { + this.title = title + if (beforeTitle != title) updateFrameTitle(title, titleMsg) + beforeTitle = title + } + + fun setTitleMessage(titleMsg: String) { + this.titleMsg = titleMsg + if (beforeTitleMsg != titleMsg) updateFrameTitle(title, titleMsg) + beforeTitleMsg = titleMsg + } + + fun updateTunerFields(fields: List) { + tunerMenuPanel.removeAll() + tunerMenuPanel.repaint() + + for (fieldPanel in fields) { + tunerMenuPanel.add(fieldPanel) + fieldPanel.showFieldPanel() + } + + tunerCollapsible.isVisible = fields.isNotEmpty() + } + + fun compilerUnsupported() { + DialogFactory.createInformation( + frame, + "Runtime pipeline builds are not supported on this JVM", + "For further info, check the EOCV-Sim docs", + "Operation failed" + ) + } + + fun selectPipelinesWorkspace() { + DialogFactory.createFileChooser(frame, DialogFactory.FileChooser.Mode.DIRECTORY_SELECT) + .addCloseListener { option, selectedFile, _ -> + if (option == JFileChooser.APPROVE_OPTION && selectedFile != null) { + if (!selectedFile.exists()) selectedFile.mkdir() + eocvSim.onMainUpdate.once { + eocvSim.workspaceManager.workspaceFile = selectedFile + } + } + } + } + + fun createVSCodeWorkspace() { + DialogFactory.createFileChooser(frame, DialogFactory.FileChooser.Mode.DIRECTORY_SELECT) + .addCloseListener { option, selectedFile, _ -> + if (option == JFileChooser.APPROVE_OPTION && selectedFile != null) { + if (!selectedFile.exists()) selectedFile.mkdir() + + if (selectedFile.isDirectory && (selectedFile.listFiles()?.size ?: 0) == 0) { + eocvSim.workspaceManager.createWorkspaceWithTemplateAsync(selectedFile, GradleWorkspaceTemplate) { + askOpenVSCode() + } + } else { + DialogFactory.createInformation( + frame, + "The selected directory must be empty", + "Select an empty directory or create a new one", + "Operation failed" + ) + } + } + } + } + + fun 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.asyncLaunch(eocvSim.workspaceManager.workspaceFile, eocvSim.scope) + } + } + } +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/FileSelector.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/FileSelector.kt index 2c9cd08d..dae49a39 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/FileSelector.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/FileSelector.kt @@ -58,7 +58,7 @@ class FileSelector(columns: Int = 18, selectDirButton.addActionListener { val frame = SwingUtilities.getWindowAncestor(this) - DialogFactory.createFileChooser(frame, mode, *fileFilters).addCloseListener { returnVal: Int, selectedFile: File?, selectedFileFilter: FileFilter? -> + DialogFactory.createFileChooser(frame, mode, "", *fileFilters).addCloseListener { returnVal: Int, selectedFile: File?, selectedFileFilter: FileFilter? -> if (returnVal == JFileChooser.APPROVE_OPTION) { lastSelectedFileFilter = selectedFileFilter lastSelectedFile = selectedFile 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 829d25c7..6b2fadab 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 @@ -197,7 +197,7 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { CrashReport.defaultCrashFileName, FileFilters.logFileFilter ).addCloseListener { OPTION, selectedFile, _ -> if(OPTION == JFileChooser.APPROVE_OPTION) { - var path = selectedFile.absolutePath + var path = selectedFile?.absolutePath ?: return@addCloseListener if (path.endsWith(File.separator)) path = path.removeSuffix(File.separator) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt index f4f9b0bd..99beb676 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt @@ -160,9 +160,36 @@ class SourceSelectorPanel(private val eocvSim: EOCVSim) : JPanel() { } } - eocvSim.inputSourceManager.onSourcesListChange { + eocvSim.inputSourceManager.onInputSourceRemoved { updateSourcesList() } + + eocvSim.inputSourceManager.onInputSourceAdded { + val name = eocvSim.inputSourceManager.lastAddedSourceName + val dispatchedByUser = eocvSim.inputSourceManager.wasLastSourceAddedByUser + + updateSourcesList() + + SwingUtilities.invokeLater { + val currentIndex = sourceSelector.selectedIndex + + if (dispatchedByUser) { + val index = getIndexOf(name) + sourceSelector.selectedIndex = index + + eocvSim.inputSourceManager.requestSetInputSource(name) + + eocvSim.onMainUpdate.once { + eocvSim.pipelineManager.requestSetPaused(false) + eocvSim.inputSourceManager.pauseIfImageTwoFrames() + } + } else { + sourceSelector.selectedIndex = currentIndex + } + + allowSourceSwitching = true + } + } } fun updateSourcesList(): Job { @@ -186,7 +213,7 @@ class SourceSelectorPanel(private val eocvSim: EOCVSim) : JPanel() { } fun getIndexOf(name: String): Int { - for (i in 0..sourceSelector.model.size) { + for (i in 0 until sourceSelector.model.size) { if (sourceSelector.model.getElementAt(i) == name) return i } 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 6f0b1846..f215630b 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 @@ -54,12 +54,8 @@ public class About { } public About(JFrame parent, EOCVSim eocvSim) { - about = new JDialog(parent); - - eocvSim.visualizer.childDialogs.add(about); initAbout(); - } private void initAbout() { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/FileAlreadyExists.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/FileAlreadyExists.java index 8f6468bf..6c46d02a 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/FileAlreadyExists.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/FileAlreadyExists.java @@ -41,9 +41,6 @@ public FileAlreadyExists(JFrame parent, EOCVSim eocvSim) { fileAlreadyExists = new JDialog(parent); this.eocvSim = eocvSim; - - eocvSim.visualizer.childDialogs.add(fileAlreadyExists); - } public UserChoice run() { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateSource.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateSource.java index 1e811378..8e49be58 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateSource.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateSource.java @@ -40,16 +40,12 @@ public class CreateSource { private volatile JFrame parent = null; public CreateSource(JFrame parent, EOCVSim eocvSim) { - chooseSource = new JDialog(parent); - this.parent = parent; this.eocvSim = eocvSim; - eocvSim.visualizer.childDialogs.add(chooseSource); initChooseSource(); - } private void initChooseSource() { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt index 95ac637f..3001da93 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt @@ -7,6 +7,7 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeout +import javax.swing.JDialog object InputSourceInitializer { @@ -39,7 +40,7 @@ object InputSourceInitializer { logger.error("InputSource initialization timed out after $TIMEOUT ms", e) } finally { job.cancel() - dialog?.destroyDialog() + dialog?.dispose() } } @@ -73,7 +74,7 @@ object InputSourceInitializer { logger.error("InputSource run timed out after $TIMEOUT ms", e) } finally { job.cancel() - dialog?.destroyDialog() + dialog?.dispose() } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt index e1f49b5f..6e30faa8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt @@ -40,8 +40,10 @@ import org.opencv.imgproc.Imgproc import java.awt.Dimension import java.io.File import java.io.IOException +import com.github.serivesmejia.eocvsim.gui.DialogFactory import org.openftc.easyopencv.MatRecycler import java.util.concurrent.CancellationException +import javax.swing.JDialog import javax.swing.SwingUtilities class InputSourceManager(val eocvSim: EOCVSim) { @@ -58,7 +60,13 @@ class InputSourceManager(val eocvSim: EOCVSim) { val inputSourceLoader = InputSourceLoader() private lateinit var matRecycler: MatRecycler - val onSourcesListChange = EventHandler("InputSourceManager-OnSourcesListChange") + val onInputSourceAdded = EventHandler("InputSourceManager-OnInputSourceAdded") + val onInputSourceRemoved = EventHandler("InputSourceManager-OnInputSourceRemoved") + + var lastAddedSourceName = "" + private set + var wasLastSourceAddedByUser = false + private set private var defaultSource = "" @@ -156,8 +164,6 @@ class InputSourceManager(val eocvSim: EOCVSim) { inputSource.eocvSim = eocvSim - eocvSim.visualizer.sourceSelectorPanel?.allowSourceSwitching = false - inputSource.name = name sources[name] = inputSource @@ -170,32 +176,10 @@ class InputSourceManager(val eocvSim: EOCVSim) { inputSourceLoader.saveInputSourcesToFile() } - onSourcesListChange.run() - - eocvSim.visualizer.sourceSelectorPanel?.let { panel -> - SwingUtilities.invokeLater { - val sourceSelector = panel.sourceSelector - - val currentSourceIndex = sourceSelector.selectedIndex - - if (dispatchedByUser) { - val index = panel.getIndexOf(name) - - sourceSelector.selectedIndex = index - - requestSetInputSource(name) - - eocvSim.onMainUpdate.once { - eocvSim.pipelineManager.requestSetPaused(false) - pauseIfImageTwoFrames() - } - } else { - sourceSelector.selectedIndex = currentSourceIndex - } + lastAddedSourceName = name + wasLastSourceAddedByUser = dispatchedByUser - panel.allowSourceSwitching = true - } - } + onInputSourceAdded.run() logger.info("Adding InputSource $inputSource (${inputSource.javaClass.simpleName})") } @@ -208,6 +192,8 @@ class InputSourceManager(val eocvSim: EOCVSim) { inputSourceLoader.deleteInputSource(sourceName) inputSourceLoader.saveInputSourcesToFile() + + onInputSourceRemoved.run() } fun setInputSource(sourceName: String?, makeDefault: Boolean): Boolean { @@ -220,6 +206,8 @@ class InputSourceManager(val eocvSim: EOCVSim) { return result } + val onInputSourceInitError = EventHandler("InputSourceManager-OnInputSourceInitError") + fun setInputSource(sourceName: String?): Boolean { val src = if (sourceName == null) { NullSource() @@ -231,10 +219,7 @@ class InputSourceManager(val eocvSim: EOCVSim) { if (src != null) { if (!InputSourceInitializer.initializeWithTimeout(src, this)) { - eocvSim.visualizer.asyncPleaseWaitDialog( - "Error while loading requested source", "Falling back to previous source", - "Close", Dimension(300, 150), true, true - ) + onInputSourceInitError.run() logger.error("Error while loading requested source ($sourceName) reported by itself (init method returned false)") @@ -298,18 +283,15 @@ class InputSourceManager(val eocvSim: EOCVSim) { eocvSim.onMainUpdate.once { setInputSource(name) } } - fun showApwdIfNeeded(sourceName: String?, job: Job?): Visualizer.AsyncPleaseWaitDialog? { + fun showApwdIfNeeded(sourceName: String?, job: Job?): JDialog? { val type = getSourceType(sourceName) if (type == SourceType.CAMERA || type == SourceType.VIDEO || type == SourceType.HTTP) { - val apwd = eocvSim.visualizer.asyncPleaseWaitDialog( - "Opening source...", null, "Cancel", - Dimension(300, 150), true - ) - - apwd.onCancel { + return DialogFactory.createInformation( + eocvSim.visualizer.frame, + "Opening source...", null, "Information", "Cancel" + ) { job?.cancel(CancellationException()) } - return apwd } return null diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt index b7a6582b..2e2fc218 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt @@ -167,7 +167,7 @@ class CompiledPipelineManager(private val pipelineManager: PipelineManager) { PipelineCompileStatus.NO_SOURCE -> { //delete jar if we had no sources, the most logical outcome in this case deleteJarFile() - if(pipelineManager.eocvSim.visualizer.hasFinishedInit()) + if(pipelineManager.eocvSim.visualizer.hasFinishedInitializing) pipelineManager.onPipelineListRefresh.run() "Build cancelled, no source files to compile $messageEnd" diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SwingApisImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SwingApisImpl.kt index 0f467db4..4660bb23 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SwingApisImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SwingApisImpl.kt @@ -124,10 +124,14 @@ class JMenuItemApiImpl(owner: EOCVSimPlugin, val internalMenuItem: JMenuItem) : class JFileChooserApiImpl(owner: EOCVSimPlugin, val internalFileChooser: DialogFactory.FileChooser) : JFileChooserApi(owner) { override fun addCloseListener(listener: (Result, File, FileFilter) -> Unit) { internalFileChooser.addCloseListener { i, file, filter -> + val dummyFilter = javax.swing.filechooser.FileNameExtensionFilter("", "") + val finalFile = file ?: File("") + val finalFilter = filter ?: dummyFilter + when(i) { - JFileChooser.APPROVE_OPTION -> listener(Result.APPROVE, file, filter) - JFileChooser.CANCEL_OPTION -> listener(Result.CANCEL, file, filter) - else -> listener(Result.ERROR, file, filter) + JFileChooser.APPROVE_OPTION -> listener(Result.APPROVE, finalFile, finalFilter) + JFileChooser.CANCEL_OPTION -> listener(Result.CANCEL, finalFile, finalFilter) + else -> listener(Result.ERROR, finalFile, finalFilter) } } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableValue.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableValue.kt index 641f8563..cebf0a10 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableValue.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableValue.kt @@ -83,13 +83,11 @@ class TunableBoolean(initialValue: Boolean, supplier: () -> Boolean, consumer: ( } class TunableEnum>(initialValue: T, val enumValues: Array, supplier: () -> T, consumer: (T) -> Unit) : TunableValue(initialValue, supplier, consumer) { override fun setAnyFromGui(guiValue: Any) { - if (value != null) { - if (value!!::class.java.isInstance(guiValue)) { - @Suppress("UNCHECKED_CAST") - setFromGui(guiValue as T) - } else { - throw IllegalArgumentException("Expected ${value!!::class.java.name} but got ${guiValue::class.java.name}") - } + if (value::class.java.isInstance(guiValue)) { + @Suppress("UNCHECKED_CAST") + setFromGui(guiValue as T) + } else { + throw IllegalArgumentException("Expected ${value::class.qualifiedName} but got ${guiValue::class.qualifiedName}") } } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt index 2ef99171..3ebe2da7 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt @@ -10,13 +10,13 @@ abstract class NumericField( target: Any, reflectionField: VirtualField, eocvSim: EOCVSim, - allowMode: AllowMode + allowMode: AllowMode, + initialValue: T ) : TunableField(target, reflectionField, eocvSim, allowMode) { - @Suppress("UNCHECKED_CAST") - protected var _value: T? = initialFieldValue as T? + protected var _value: T = initialValue - protected val tunableValue by lazy { TunableNumber(_value?.toDouble() ?: 0.0, { _value?.toDouble() ?: 0.0 }, { updateNumber(it) }) } + protected val tunableValue by lazy { TunableNumber(_value.toDouble(), { _value.toDouble() }, { updateNumber(it) }) } override val tunableValues by lazy { listOf(tunableValue) } @@ -24,7 +24,7 @@ abstract class NumericField( private fun updateNumber(newValue: Double) { _value = createNumber(newValue) - setPipelineFieldValue(_value!!) + setPipelineFieldValue(_value) } override fun init() { @@ -32,11 +32,9 @@ abstract class NumericField( } override fun refreshPipelineObject() { - @Suppress("UNCHECKED_CAST") - val current = reflectionField.get() as T - _value = current + _value = createNumber((reflectionField.get() as? Number)?.toDouble() ?: 0.0) } override val value: T - get() = _value!! + get() = _value } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt index 6e274adf..0651ce6f 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt @@ -9,11 +9,7 @@ class DoubleField( instance: Any, reflectionField: VirtualField, eocvSim: EOCVSim -) : NumericField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL) { - - init { - _value = initialFieldValue as? Double ?: 0.0 - } +) : NumericField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL, reflectionField.get() as? Double ?: 0.0) { override fun createNumber(value: Double): Double = value diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt index b4c308c6..87ed044e 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt @@ -9,11 +9,7 @@ class FloatField( instance: Any, reflectionField: VirtualField, eocvSim: EOCVSim -) : NumericField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL) { - - init { - _value = initialFieldValue as? Float ?: 0.0f - } +) : NumericField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL, reflectionField.get() as? Float ?: 0.0f) { override fun createNumber(value: Double): Float = value.toFloat() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt index 3c6b8c80..078eb065 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt @@ -9,11 +9,7 @@ class IntegerField( instance: Any, reflectionField: VirtualField, eocvSim: EOCVSim -) : NumericField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS) { - - init { - _value = initialFieldValue as? Int ?: 0 - } +) : NumericField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS, reflectionField.get() as? Int ?: 0) { override fun createNumber(value: Double): Int = value.toInt() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt index f8705ba3..9d95de87 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt @@ -9,11 +9,7 @@ class LongField( instance: Any, reflectionField: VirtualField, eocvSim: EOCVSim -) : NumericField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS) { - - init { - _value = initialFieldValue as? Long ?: 0L - } +) : NumericField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS, reflectionField.get() as? Long ?: 0L) { override fun createNumber(value: Double): Long = value.toLong() 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 16da71c7..a3005239 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 @@ -90,7 +90,7 @@ class PluginManager(val eocvSim: EOCVSim) { } } - appender!! + appender } val repositoryManager by lazy { diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt b/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt index cdb12386..24c76db7 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt +++ b/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt @@ -45,7 +45,10 @@ import java.util.concurrent.TimeUnit import javax.swing.JComponent import javax.swing.SwingUtilities -class SwingOpenCvViewport(size: Size, fpsMeterDescriptor: String = "deltacv Vision") : OpenCvViewport, MatPoster { +class SwingOpenCvViewport( + private val size: Size, + fpsMeterDescriptor: String = "deltacv Vision" +) : OpenCvViewport, MatPoster { private val syncObj = Any() @@ -60,6 +63,8 @@ class SwingOpenCvViewport(size: Size, fpsMeterDescriptor: String = "deltacv Visi @Volatile private var useGpuCanvas = false + private var isInitialized = false + var dark = false private enum class RenderingState { @@ -85,7 +90,12 @@ class SwingOpenCvViewport(size: Size, fpsMeterDescriptor: String = "deltacv Visi private var renderHook: RenderHook? = null - init { + fun init() { + if(isInitialized) { + logger.warn("init() called on SwingOpenCvViewport, but it was already initialized! Ignoring redundant call.") + return + } + visionPreviewFrameQueue.setEvictAction { value: MatRecycler.RecyclableMat? -> /* * If a Mat is evicted from the queue, we need @@ -116,6 +126,9 @@ class SwingOpenCvViewport(size: Size, fpsMeterDescriptor: String = "deltacv Visi } }) + + isInitialized = true + setSize(size.width.toInt(), size.height.toInt()) } @@ -163,6 +176,7 @@ class SwingOpenCvViewport(size: Size, fpsMeterDescriptor: String = "deltacv Visi } override fun setFpsMeterEnabled(enabled: Boolean) {} + override fun resume() { synchronized(syncObj) { userRequestedPause = false @@ -235,6 +249,10 @@ class SwingOpenCvViewport(size: Size, fpsMeterDescriptor: String = "deltacv Visi * Called with syncObj held */ fun checkState() { + if(!isInitialized) { + throw IllegalStateException("checkState() called before SwingOpenCvViewport was initialized! Call init() first.") + } + /* * If the surface isn't ready, don't do anything */ diff --git a/Vision/src/main/java/org/opencv/android/Utils.java b/Vision/src/main/java/org/opencv/android/Utils.java index 991aae80..3aafb692 100644 --- a/Vision/src/main/java/org/opencv/android/Utils.java +++ b/Vision/src/main/java/org/opencv/android/Utils.java @@ -95,7 +95,7 @@ private static void nBitmapToMat2(Bitmap b, Mat mat, boolean unPremultiplyAlpha) } } - private static ThreadLocal> threadLocalm2bReusableBuffers = ThreadLocal.withInitial(WeakHashMap::new); + private static final ThreadLocal> m2bReusableBuffers = ThreadLocal.withInitial(WeakHashMap::new); private static void nMatToBitmap2(Mat src, Bitmap b, boolean premultiplyAlpha) { Mat tmp; @@ -127,7 +127,7 @@ private static void nMatToBitmap2(Mat src, Bitmap b, boolean premultiplyAlpha) { int size = tmp.rows() * tmp.cols() * tmp.channels(); - WeakHashMap m2bArrays = threadLocalm2bReusableBuffers.get(); + WeakHashMap m2bArrays = m2bReusableBuffers.get(); byte[] m2bData = m2bArrays.get(size); From 409d7c981b15dd3227c0975dcaa4e15d9f16521c Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 21 Apr 2026 18:20:38 -0600 Subject: [PATCH 07/40] Migrate to koin dependency injection & rewrite major classes into kotlin --- EOCV-Sim/build.gradle | 3 + .../github/serivesmejia/eocvsim/EOCVSim.kt | 156 ++------- .../com/github/serivesmejia/eocvsim/Main.kt | 13 +- .../{ConfigManager.java => ConfigManager.kt} | 131 ++++---- .../serivesmejia/eocvsim/di/EOCVSimModule.kt | 69 ++++ .../serivesmejia/eocvsim/gui/DialogFactory.kt | 94 +++--- .../github/serivesmejia/eocvsim/gui/Icons.kt | 13 +- .../serivesmejia/eocvsim/gui/Visualizer.kt | 114 ++++--- .../gui/component/input/FileSelector.kt | 8 +- .../component/tuner/TunableFieldPanel.java | 235 -------------- .../gui/component/tuner/TunableFieldPanel.kt | 199 ++++++++++++ .../tuner/TunableFieldPanelConfig.kt | 52 +-- .../tuner/TunableFieldPanelOptions.kt | 41 ++- .../tuner/element/TunableComboBox.java | 78 ----- .../tuner/element/TunableComboBox.kt | 55 ++++ .../component/tuner/element/TunableSlider.kt | 26 +- .../tuner/element/TunableTextField.java | 200 ------------ .../tuner/element/TunableTextField.kt | 154 +++++++++ .../component/visualizer/CreateSourcePanel.kt | 8 +- .../visualizer/InputSourceDropTarget.kt | 8 +- .../gui/component/visualizer/SidebarPanel.kt | 10 +- .../component/visualizer/TelemetryPanel.kt | 19 +- .../gui/component/visualizer/TopMenuBar.kt | 61 +++- .../visualizer/opmode/OpModeControlsPanel.kt | 34 +- .../visualizer/opmode/OpModeSelectorPanel.kt | 66 ++-- .../opmode/SidebarOpModeTabPanel.kt | 14 +- .../pipeline/PipelineSelectorButtonsPanel.kt | 35 +- .../pipeline/PipelineSelectorPanel.kt | 54 ++-- .../pipeline/SidebarPipelineTabPanel.kt | 24 +- .../pipeline/SourceSelectorPanel.kt | 63 ++-- .../eocvsim/gui/dialog/About.java | 173 ---------- .../serivesmejia/eocvsim/gui/dialog/About.kt | 161 +++++++++ .../eocvsim/gui/dialog/Configuration.kt | 29 +- .../eocvsim/gui/dialog/CreateWorkspace.kt | 16 +- .../eocvsim/gui/dialog/FileAlreadyExists.java | 91 ------ .../eocvsim/gui/dialog/FileAlreadyExists.kt | 82 +++++ .../serivesmejia/eocvsim/gui/dialog/Output.kt | 40 ++- .../eocvsim/gui/dialog/PluginOutput.kt | 30 +- .../eocvsim/gui/dialog/iama/IAmA.kt | 26 +- .../gui/dialog/iama/IAmAFirstRobotics.kt | 18 +- .../gui/dialog/iama/IAmAGeneralPublic.kt | 20 +- .../gui/dialog/iama/IAmAPaperVision.kt | 36 ++- .../gui/dialog/source/CreateCameraSource.kt | 51 +-- .../gui/dialog/source/CreateHttpSource.kt | 27 +- .../gui/dialog/source/CreateImageSource.kt | 28 +- .../gui/dialog/source/CreateSource.java | 111 ------- .../gui/dialog/source/CreateSourceEx.kt | 22 +- .../gui/dialog/source/CreateVideoSource.kt | 33 +- .../eocvsim/gui/util/GuiUtil.java | 226 ------------- .../serivesmejia/eocvsim/gui/util/GuiUtil.kt | 156 +++++++++ .../eocvsim/input/InputSource.java | 108 ------- .../serivesmejia/eocvsim/input/InputSource.kt | 85 +++++ .../eocvsim/input/InputSourceInitializer.kt | 181 ++++++----- .../eocvsim/input/InputSourceLoader.java | 178 ---------- .../eocvsim/input/InputSourceLoader.kt | 169 ++++++++++ .../eocvsim/input/InputSourceManager.kt | 46 +-- .../eocvsim/input/SourceType.java | 82 ----- .../serivesmejia/eocvsim/input/SourceType.kt | 72 +++++ .../eocvsim/input/source/CameraSource.java | 306 ------------------ .../eocvsim/input/source/CameraSource.kt | 285 ++++++++++++++++ .../input/source/CameraSourceAdapter.java | 26 -- .../input/source/CameraSourceAdapter.kt | 24 ++ .../eocvsim/input/source/HttpSource.java | 239 -------------- .../eocvsim/input/source/HttpSource.kt | 225 +++++++++++++ .../eocvsim/input/source/ImageSource.java | 195 ----------- .../eocvsim/input/source/ImageSource.kt | 145 +++++++++ .../source/{NullSource.java => NullSource.kt} | 130 +++----- .../eocvsim/input/source/VideoSource.java | 236 -------------- .../eocvsim/input/source/VideoSource.kt | 202 ++++++++++++ .../eocvsim/output/RecordingManager.kt | 100 ++++++ .../eocvsim/pipeline/PipelineManager.kt | 74 +++-- .../compiler/CompiledPipelineManager.kt | 48 ++- .../plugin/api/impl/DialogFactoryApiImpl.kt | 35 +- .../eocvsim/plugin/api/impl/EOCVSimApiImpl.kt | 33 +- .../plugin/api/impl/InputSourceApisImpl.kt | 10 +- .../eocvsim/tuner/TunableField.kt | 12 +- .../eocvsim/tuner/TunableFieldRegistry.kt | 33 +- .../eocvsim/tuner/TunableValue.kt | 2 +- .../eocvsim/tuner/TunerManager.kt | 24 +- .../eocvsim/tuner/field/BooleanField.kt | 6 +- .../eocvsim/tuner/field/EnumField.kt | 4 +- .../eocvsim/tuner/field/NumericField.kt | 4 +- .../eocvsim/tuner/field/StringField.kt | 6 +- .../eocvsim/tuner/field/cv/PointField.kt | 6 +- .../eocvsim/tuner/field/cv/RectField.kt | 5 +- .../eocvsim/tuner/field/cv/ScalarField.kt | 6 +- .../tuner/field/numeric/DoubleField.kt | 6 +- .../eocvsim/tuner/field/numeric/FloatField.kt | 6 +- .../tuner/field/numeric/IntegerField.kt | 6 +- .../eocvsim/tuner/field/numeric/LongField.kt | 6 +- .../eocvsim/util/ClasspathScan.kt | 14 +- .../handling/CrashReportOutputMain.kt | 3 +- .../eocvsim/workspace/WorkspaceManager.kt | 108 ++++--- .../eocvsim/input/VisionInputSource.kt | 2 +- .../input/VisionInputSourceProvider.kt | 6 +- .../control/CameraSourceExposureControl.kt | 2 +- .../eocvsim/plugin/loader/PluginManager.kt | 43 ++- .../repository/PluginRepositoryManager.kt | 9 +- build.gradle | 1 + 99 files changed, 3487 insertions(+), 3480 deletions(-) rename EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/{ConfigManager.java => ConfigManager.kt} (52%) create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/di/EOCVSimModule.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableComboBox.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableComboBox.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/FileAlreadyExists.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/FileAlreadyExists.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateSource.java delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/GuiUtil.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/GuiUtil.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceLoader.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceLoader.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/SourceType.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/SourceType.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSourceAdapter.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSourceAdapter.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.kt rename EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/{NullSource.java => NullSource.kt} (54%) delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/RecordingManager.kt diff --git a/EOCV-Sim/build.gradle b/EOCV-Sim/build.gradle index 6be391de..bb1bbc8d 100644 --- a/EOCV-Sim/build.gradle +++ b/EOCV-Sim/build.gradle @@ -76,6 +76,9 @@ dependencies { implementation 'org.jetbrains.kotlin:kotlin-stdlib' + implementation(platform("io.insert-koin:koin-bom:$koin_version")) + implementation("io.insert-koin:koin-core") + implementation "org.eclipse.jdt:ecj:3.21.0" api "org.openpnp:opencv:$opencv_version" 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 f731bcb0..52990cc8 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 @@ -29,9 +29,12 @@ import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.gui.dialog.FileAlreadyExists import com.github.serivesmejia.eocvsim.input.InputSourceManager +import com.github.serivesmejia.eocvsim.output.RecordingManager import com.github.serivesmejia.eocvsim.output.VideoRecordingSession + import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.pipeline.PipelineSource +import com.github.serivesmejia.eocvsim.pipeline.compiler.CompiledPipelineManager import com.github.serivesmejia.eocvsim.tuner.TunerManager import com.github.serivesmejia.eocvsim.util.ClasspathScan import com.github.serivesmejia.eocvsim.util.FileFilters @@ -54,6 +57,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import nu.pattern.OpenCV +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named import org.opencv.core.Mat import org.opencv.core.Size import org.openftc.easyopencv.OpenCvViewport @@ -74,7 +80,7 @@ import kotlin.system.exitProcess * @param params the parameters to initialize the simulator with * @see Parameters */ -class EOCVSim(val params: Parameters = Parameters()) { +class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { companion object { const val VERSION = Build.versionString @@ -88,8 +94,6 @@ class EOCVSim(val params: Parameters = Parameters()) { @JvmField val DEFAULT_EOCV_SIZE = Size(DEFAULT_EOCV_WIDTH.toDouble(), DEFAULT_EOCV_HEIGHT.toDouble()) - private var hasScanned = false - private val classpathScan = ClasspathScan() val logger by loggerFor(EOCVSim::class) @@ -147,35 +151,25 @@ class EOCVSim(val params: Parameters = Parameters()) { * posted by the different components of the simulator * @see EventHandler */ - @JvmField val onMainUpdate = EventHandler("OnMainUpdate") + val onMainUpdate: EventHandler by inject(named("onMainLoop")) + val onRestartRequested: EventHandler by inject(named("onRestartRequested")) + val onDestroyRequested: EventHandler by inject(named("onDestroyRequested")) + /** * The visualizer instance in charge of managing the GUI * and the viewport where the pipeline output is shown * @see Visualizer */ - val visualizer = Visualizer(this) + val visualizer: Visualizer by inject() - /** - * The configuration manager instance in charge of managing - * the configuration of the simulator from the config json file - */ - @JvmField val configManager = ConfigManager() + val configManager: ConfigManager by inject() + val inputSourceManager: InputSourceManager by inject() + val pluginManager: PluginManager by inject() - /** - * The input source manager instance in charge of managing input sources - * and their loading, as well as the current input source. - * Input Sources are the sources of the frames that the pipeline processes - * they can be from a webcam, a video or image file, etc. - * @see InputSourceManager - */ - @JvmField val inputSourceManager = InputSourceManager(this) + val recordingManager: RecordingManager by inject() + val dialogFactory: DialogFactory by inject() - /** - * Manager in charge of loading and managing plugins - * @see PluginManager - */ - @JvmField val pluginManager = PluginManager(this) /** * The pipeline statistics calculator instance in charge of @@ -183,25 +177,25 @@ class EOCVSim(val params: Parameters = Parameters()) { * of the current pipeline * @see PipelineStatisticsCalculator */ - @JvmField val pipelineStatisticsCalculator = PipelineStatisticsCalculator() + val pipelineStatisticsCalculator: PipelineStatisticsCalculator by inject() /** * The pipeline manager instance in charge of managing pipelines * and their execution, as well as making sure the pipeline * does not take too long to process a frame */ - @JvmField val pipelineManager = PipelineManager(this, pipelineStatisticsCalculator) + val pipelineManager: PipelineManager by inject() /** * The tuner manager instance in charge of managing pipeline and processor * tunable variables and their values that can be changed in runtime */ - @JvmField val tunerManager = TunerManager(this) + val tunerManager: TunerManager by inject() /** * The workspace manager instance in charge of managing user workspaces */ - @JvmField val workspaceManager = WorkspaceManager(this) + val workspaceManager: WorkspaceManager by inject() /** * The current configuration of the simulator @@ -210,20 +204,7 @@ class EOCVSim(val params: Parameters = Parameters()) { */ val config: Config get() = configManager.config - /** - * The classpath scanner instance containing all the scanned classes - * From OpenCvPipeline, VisionProcessor, TunableField and OpMode classes - * @see ClasspathScan - */ - val classpathScan get() = Companion.classpathScan - - /** - * The current recording session of the simulator - * This allows the user to record the output of the pipeline - * and save it to a file - * @see VideoRecordingSession - */ - var currentRecordingSession: VideoRecordingSession? = null + val classpathScan: ClasspathScan by inject() /** * Utility in charge of limiting the FPS of the simulator @@ -268,6 +249,10 @@ class EOCVSim(val params: Parameters = Parameters()) { fun init() { eocvSimThread = Thread.currentThread() + // Wire up lifecycle events so components can trigger restart/destroy without injecting EOCVSim + onRestartRequested { restart() } + onDestroyRequested { destroy() } + if (!EOCVSimFolder.couldLock) { logger.error( "Couldn't finally claim lock file in \"${EOCVSimFolder.absolutePath}\"! " + "Is the folder opened by another EOCV-Sim instance?" @@ -286,7 +271,7 @@ class EOCVSim(val params: Parameters = Parameters()) { logger.info("Confirmed claiming of the lock file in ${EOCVSimFolder.absolutePath}") } - DialogFactory.createSplashScreen(visualizer.onInitFinished) + dialogFactory.createSplashScreen(visualizer.onInitFinished) logger.info("-- Initializing EasyOpenCV Simulator v$VERSION ($hexCode) --") @@ -295,10 +280,7 @@ class EOCVSim(val params: Parameters = Parameters()) { //loading native lib only once in the app runtime loadOpenCvLib(params.opencvNativeLibrary) - if (!hasScanned) { - classpathScan.asyncScan(scope) - hasScanned = true - } + classpathScan.asyncScan(scope) configManager.init() @@ -314,11 +296,11 @@ class EOCVSim(val params: Parameters = Parameters()) { if(!config.flags.contains("hasShownIamA") || config.flags["hasShownIamA"] == false) { // Initial dialog to introduce our cool stuff to the user - DialogFactory.createIAmA(visualizer) + dialogFactory.createIAmA() } 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) + dialogFactory.createIAmAPaperVision(false) } else if(config.flags["prefersPaperVision"] == true) { // if the user prefers PaperVision, switch to it upon start up val indexOfTab = visualizer.sidebarPanel.indexOfTab("PaperVision") @@ -340,7 +322,7 @@ class EOCVSim(val params: Parameters = Parameters()) { //shows a warning when a pipeline gets "stuck" pipelineManager.onPipelineTimeout { - DialogFactory.createInformation( + dialogFactory.createInformation( visualizer.frame, "Current pipeline took too long to ${pipelineManager.lastPipelineAction}", "Falling back to DefaultPipeline", @@ -481,8 +463,8 @@ class EOCVSim(val params: Parameters = Parameters()) { pluginManager.disablePlugins() //stop recording session if there's currently an ongoing one - currentRecordingSession?.stopRecordingSession() - currentRecordingSession?.discardVideo() + recordingManager.stopRecordingSession() + logger.info("Trying to save config file...") @@ -523,82 +505,14 @@ class EOCVSim(val params: Parameters = Parameters()) { destroy(DestroyReason.RESTART) } - /** - * Starts a recording session to record the output of the pipeline - * to a video file in real-time which will be prompted to the user - * to save it to a file after stopping the recording session. - */ - fun startRecordingSession() { - if (currentRecordingSession == null) { - currentRecordingSession = VideoRecordingSession( - config.videoRecordingFps.fps.toDouble(), config.videoRecordingSize - ) - - currentRecordingSession!!.startRecordingSession() - logger.info("Recording session started") - - pipelineManager.pipelineOutputPosters.add(currentRecordingSession!!.matPoster) - } - } - - /** - * Stops the current recording session and prompts the user to save the recorded video - */ - fun stopRecordingSession() { - currentRecordingSession?.let { itVideo -> - - visualizer.pipelineSelectorPanel.buttonsPanel.pipelineRecordBtt.isEnabled = false - - itVideo.stopRecordingSession() - pipelineManager.pipelineOutputPosters.remove(itVideo.matPoster) - - logger.info("Recording session stopped") - - DialogFactory.createFileChooser( - visualizer.frame, DialogFactory.FileChooser.Mode.SAVE_FILE_SELECT, "", FileFilters.recordedVideoFilter - ).addCloseListener { _: Int, file: File?, selectedFileFilter: FileFilter? -> - onMainUpdate.once { - if (file != null) { - var correctedFile = file - val extension = SysUtil.getExtensionByStringHandling(file.name) - - if (selectedFileFilter is FileNameExtensionFilter) { //if user selected an extension - //get selected extension - correctedFile = File(file.absolutePath + "." + selectedFileFilter.extensions[0]) - } else if (extension.isPresent) { - if (!extension.get().equals("avi", true)) { - correctedFile = File(file.absolutePath + ".avi") - } - } else { - correctedFile = File(file.absolutePath + ".avi") - } - - if (correctedFile.exists()) { - SwingUtilities.invokeLater { - if (DialogFactory.createFileAlreadyExistsDialog(this@EOCVSim) == FileAlreadyExists.UserChoice.REPLACE) { - onMainUpdate.once { itVideo.saveTo(correctedFile) } - } - } - } else { - itVideo.saveTo(correctedFile) - } - } else { - itVideo.discardVideo() - } - - currentRecordingSession = null - visualizer.pipelineSelectorPanel.buttonsPanel.pipelineRecordBtt.isEnabled = true - } - } - } - } /** * Checks if the simulator is currently recording * @return true if the simulator is currently recording, false otherwise */ - fun isCurrentlyRecording() = currentRecordingSession?.isRecording == true + fun isCurrentlyRecording() = recordingManager.isCurrentlyRecording() + /** * Updates the visualizer title message 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 5300382d..e0c619c9 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 @@ -23,7 +23,11 @@ package com.github.serivesmejia.eocvsim +import com.github.serivesmejia.eocvsim.di.makeEOCVSimModule import com.github.serivesmejia.eocvsim.pipeline.PipelineSource +import org.koin.core.context.GlobalContext +import org.koin.core.component.KoinComponent +import org.koin.dsl.module import picocli.CommandLine import java.io.File import java.nio.file.Paths @@ -108,7 +112,14 @@ class EOCVSimCommandInterface : Runnable { parameters.opencvNativeLibrary = checkPath("OpenCV Native", opencvNativePath!!, false) } - EOCVSim(parameters).init() + GlobalContext.startKoin { + modules(makeEOCVSimModule(), module { single { parameters } }) + } + + val eocvSim = EOCVSim(parameters) + GlobalContext.loadKoinModules(module { single { eocvSim } }) + + eocvSim.init() } private fun checkPath(parameter: String, path: String, shouldBeDirectory: Boolean): File { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt similarity index 52% rename from EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.java rename to EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt index d8c1605a..c4e3a370 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt @@ -1,67 +1,64 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.config; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ConfigManager { - - public final ConfigLoader configLoader = new ConfigLoader(); - private Config config; - - Logger logger = LoggerFactory.getLogger(getClass()); - - public void init() { - logger.info("Initializing..."); - - try { - config = configLoader.loadFromFile(); - if (config == null) { - logger.error("Error while parsing config file, it will be replaced and fixed, but the user configurations will be reset"); - throw new NullPointerException(); //for it to be caught later and handle the creation of a new config - } else { - logger.info("Loaded config from file successfully"); - } - } catch (Exception ex) { //handles FileNotFoundException & a NullPointerException thrown above - config = new Config(); - logger.info("Creating config file..."); - configLoader.saveToFile(config); - } - - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - logger.info("SHUTDOWN - Saving config to file..."); - saveToFile(); - })); - } - - public void saveToFile() { - configLoader.saveToFile(config); - } - - public Config getConfig() { - return config; - } - -} +/* + * Copyright (c) 2026 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.config + +import io.github.deltacv.common.util.loggerForThis + +class ConfigManager { + + val configLoader = ConfigLoader() + var config: Config = Config() + private set + + private val logger by loggerForThis() + + fun init() { + logger.info("Initializing...") + + try { + val loadedConfig = configLoader.loadFromFile() + if (loadedConfig == null) { + logger.error("Error while parsing config file, it will be replaced and fixed, but the user configurations will be reset") + throw NullPointerException() // for it to be caught later and handle the creation of a new config + } else { + config = loadedConfig + logger.info("Loaded config from file successfully") + } + } catch (ex: Exception) { // handles FileNotFoundException & a NullPointerException thrown above + config = Config() + logger.info("Creating config file...") + configLoader.saveToFile(config) + } + + Runtime.getRuntime().addShutdownHook(Thread { + logger.info("SHUTDOWN - Saving config to file...") + saveToFile() + }) + } + + fun saveToFile() { + configLoader.saveToFile(config) + } + +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/di/EOCVSimModule.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/di/EOCVSimModule.kt new file mode 100644 index 00000000..b0ea9f3a --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/di/EOCVSimModule.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.di + +import com.github.serivesmejia.eocvsim.gui.Visualizer +import com.github.serivesmejia.eocvsim.input.InputSourceManager +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import com.github.serivesmejia.eocvsim.tuner.TunerManager +import com.github.serivesmejia.eocvsim.config.ConfigManager +import com.github.serivesmejia.eocvsim.input.InputSourceInitializer +import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager +import com.github.serivesmejia.eocvsim.pipeline.compiler.CompiledPipelineManager +import com.github.serivesmejia.eocvsim.util.ClasspathScan +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.gui.DialogFactory +import io.github.deltacv.eocvsim.plugin.loader.PluginManager + +import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import org.koin.core.qualifier.named +import org.koin.dsl.module + +fun makeEOCVSimModule() = module { + single { PipelineStatisticsCalculator() } + single { ConfigManager() } + single { ClasspathScan() } + + // global scope for launching coroutines within the app + single { CoroutineScope(SupervisorJob() + Dispatchers.Default) } + + single { WorkspaceManager() } + single { PipelineManager() } + single { InputSourceManager() } + single { InputSourceInitializer() } + single { TunerManager() } + single { PluginManager() } + single { CompiledPipelineManager() } + single { Visualizer() } + single { DialogFactory() } + single { com.github.serivesmejia.eocvsim.output.RecordingManager() } + + + single(named("onMainLoop")) { EventHandler("MainLoop") } + single(named("onRestartRequested")) { EventHandler("RestartRequested") } + single(named("onDestroyRequested")) { EventHandler("DestroyRequested") } +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt index 39e75c3b..5a7b4725 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt @@ -24,6 +24,7 @@ package com.github.serivesmejia.eocvsim.gui import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.gui.dialog.* import com.github.serivesmejia.eocvsim.gui.dialog.SplashScreen // Explicit import to resolve reference import com.github.serivesmejia.eocvsim.gui.dialog.iama.IAmA @@ -32,6 +33,7 @@ import com.github.serivesmejia.eocvsim.gui.dialog.source.* import com.github.serivesmejia.eocvsim.input.SourceType import com.github.serivesmejia.eocvsim.util.event.EventHandler import io.github.deltacv.eocvsim.plugin.loader.PluginManager +import kotlinx.coroutines.CoroutineScope import java.awt.* import java.io.File import java.util.* @@ -39,9 +41,18 @@ import javax.swing.* import javax.swing.filechooser.FileFilter import javax.swing.filechooser.FileNameExtensionFilter -object DialogFactory { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named + +class DialogFactory : KoinComponent { + + val visualizer: Visualizer by inject() + val pluginManager: PluginManager by inject() + val configManager: ConfigManager by inject() + val onRestartRequested: EventHandler by inject(named("onRestartRequested")) + val scope: CoroutineScope by inject() - @JvmStatic fun createYesOrNo(parent: Component?, message: String, submessage: String, result: (Int) -> Unit) { val panel = JPanel() val label1 = JLabel(message) @@ -64,7 +75,6 @@ object DialogFactory { } } - @JvmStatic @JvmOverloads fun createInformation( parent: Component?, @@ -101,7 +111,6 @@ object DialogFactory { return dialog } - @JvmStatic fun createFileChooser( parent: Component?, mode: FileChooser.Mode?, @@ -113,109 +122,92 @@ object DialogFactory { return fileChooser } - @JvmStatic fun createFileChooser( parent: Component?, mode: FileChooser.Mode?, vararg filters: FileFilter? ): FileChooser = createFileChooser(parent, mode, "", *filters) - @JvmStatic fun createFileChooser(parent: Component?, vararg filters: FileFilter?): FileChooser = createFileChooser(parent, null, "", *filters) - @JvmStatic - @JvmOverloads - fun createSourceDialog(eocvSim: EOCVSim, type: SourceType, initialFile: File? = null) { + fun createSourceDialog(type: SourceType, initialFile: File? = null) { invokeLater { when (type) { - SourceType.IMAGE -> CreateImageSource(eocvSim.visualizer.frame, eocvSim, initialFile) - SourceType.CAMERA -> CreateCameraSource(eocvSim.visualizer.frame, eocvSim) - SourceType.VIDEO -> CreateVideoSource(eocvSim.visualizer.frame, eocvSim, initialFile) - SourceType.HTTP -> CreateHttpSource(eocvSim.visualizer.frame, eocvSim) + SourceType.IMAGE -> CreateImageSource(initialFile) + SourceType.CAMERA -> CreateCameraSource() + SourceType.VIDEO -> CreateVideoSource(initialFile) + SourceType.HTTP -> CreateHttpSource() else -> {} } } } - @JvmStatic - fun createSourceExDialog(eocvSim: EOCVSim) { - invokeLater { CreateSourceEx(eocvSim.visualizer.frame, eocvSim.visualizer) } + fun createSourceExDialog() { + invokeLater { CreateSourceEx() } } - @JvmStatic - fun createConfigDialog(eocvSim: EOCVSim) { - invokeLater { Configuration(eocvSim.visualizer.frame, eocvSim) } + fun createConfigDialog() { + invokeLater { Configuration() } } - @JvmStatic - fun createAboutDialog(eocvSim: EOCVSim) { - invokeLater { About(eocvSim.visualizer.frame, eocvSim) } + fun createAboutDialog() { + invokeLater { About() } } - @JvmStatic - @JvmOverloads - fun createOutput(eocvSim: EOCVSim, wasManuallyOpened: Boolean = false) { + fun createOutput(wasManuallyOpened: Boolean = false) { invokeLater { if (!Output.isAlreadyOpened) { - Output(eocvSim.visualizer.frame, eocvSim, Output.latestIndex, wasManuallyOpened) + Output(Output.latestIndex, wasManuallyOpened) } } } - @JvmStatic - fun createBuildOutput(eocvSim: EOCVSim) { + fun createBuildOutput() { invokeLater { if (!Output.isAlreadyOpened) { - Output(eocvSim.visualizer.frame, eocvSim, 1) + Output(1) } } } - @JvmStatic - fun createPipelineOutput(eocvSim: EOCVSim) { + fun createPipelineOutput() { invokeLater { if (!Output.isAlreadyOpened) { - Output(eocvSim.visualizer.frame, eocvSim, 0) + Output(0) } } } - @JvmStatic - fun createMavenOutput(manager: PluginManager, onContinue: Runnable?): AppendDelegate { + fun createMavenOutput(onContinue: Runnable?): AppendDelegate { val delegate = AppendDelegate() - invokeLater { PluginOutput(delegate, manager, manager.eocvSim, onContinue ?: Runnable { }) } + invokeLater { PluginOutput(delegate, pluginManager, onRestartRequested, configManager, scope, onContinue ?: Runnable { }) } + return delegate } - @JvmStatic fun createSplashScreen(closeHandler: EventHandler?) { invokeLater { SplashScreen(closeHandler) } } - @JvmStatic - fun createIAmA(visualizer: Visualizer) { - invokeLater { IAmA(visualizer.frame, visualizer) } + fun createIAmA() { + invokeLater { IAmA() } } - @JvmStatic - fun createIAmAPaperVision(visualizer: Visualizer, showWorkspacesButton: Boolean) { - invokeLater { IAmAPaperVision(visualizer.frame, visualizer, false, showWorkspacesButton) } + fun createIAmAPaperVision(showWorkspacesButton: Boolean) { + invokeLater { IAmAPaperVision(false, showWorkspacesButton) } } - @JvmStatic - fun createWorkspace(visualizer: Visualizer) { - invokeLater { CreateWorkspace(visualizer.frame, visualizer) } + fun createWorkspace() { + invokeLater { CreateWorkspace() } } - @JvmStatic - fun createCrashReport(visualizer: Visualizer?, crash: String?) { - invokeLater { CrashReportOutput(visualizer?.frame, crash ?: "") } + fun createCrashReport(crash: String?) { + invokeLater { CrashReportOutput(visualizer.frame, crash ?: "") } } - @JvmStatic - fun createFileAlreadyExistsDialog(eocvSim: EOCVSim): FileAlreadyExists.UserChoice { - return FileAlreadyExists(eocvSim.visualizer.frame, eocvSim).run() + fun createFileAlreadyExistsDialog(): FileAlreadyExists.UserChoice { + return FileAlreadyExists().run() } private fun invokeLater(runn: Runnable) { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Icons.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Icons.kt index 0caf2086..f11c66e1 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Icons.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Icons.kt @@ -28,9 +28,16 @@ import io.github.deltacv.common.util.loggerForThis import io.github.deltacv.vision.external.gui.util.ImgUtil import java.awt.image.BufferedImage import java.util.NoSuchElementException +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import javax.swing.ImageIcon -object Icons { + +object Icons : KoinComponent { + + private val visualizer: Visualizer by inject() + + private val bufferedImages = HashMap() @@ -94,6 +101,8 @@ object Icons { GuiUtil.invertBufferedImageColors(buffImg) } + + bufferedImages[name] = Image(buffImg, allowInvert) icons[name] = NamedImageIcon(name, buffImg) } @@ -120,6 +129,8 @@ object Icons { } } + + data class Image(val img: BufferedImage, val allowInvert: Boolean) data class FutureIcon(val name: String, val resourcePath: String, val allowInvert: Boolean) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt index f93db9b5..7b855829 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt @@ -26,6 +26,7 @@ package com.github.serivesmejia.eocvsim.gui import com.formdev.flatlaf.FlatLaf import com.github.serivesmejia.eocvsim.Build import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.gui.component.CollapsiblePanelX import com.github.serivesmejia.eocvsim.gui.component.tuner.ColorPicker import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel @@ -38,6 +39,7 @@ import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.Pipelin import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SidebarPipelineTabPanel import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SourceSelectorPanel import com.github.serivesmejia.eocvsim.gui.theme.Theme +import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.github.serivesmejia.eocvsim.pipeline.compiler.PipelineCompiler import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.workspace.util.VSCodeLauncher @@ -53,20 +55,32 @@ import java.awt.event.MouseEvent import java.awt.event.WindowAdapter import java.awt.event.WindowEvent import javax.swing.* - -class Visualizer(val eocvSim: EOCVSim) { +import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import org.koin.core.qualifier.named +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import kotlinx.coroutines.CoroutineScope + +class Visualizer : KoinComponent { + + val onMainUpdate: EventHandler by inject(named("onMainLoop")) + val onDestroyRequested: EventHandler by inject(named("onDestroyRequested")) + val pipelineManager: PipelineManager by inject() + val inputSourceManager: InputSourceManager by inject() + val configManager: ConfigManager by inject() + val workspaceManager: WorkspaceManager by inject() + val dialogFactory: DialogFactory by inject() + val scope: CoroutineScope by inject() val onInitFinished = EventHandler("OnVisualizerInitFinish") val onPluginGuiAttachment = EventHandler("OnPluginGuiAttachment") - val childFrames = ArrayList() - val childDialogs = ArrayList() - lateinit var frame: JFrame private set private val fpsMeterDescriptor: String - get() = "deltacv EOCV-Sim v" + Build.standardVersionString + if (Build.isDev) "-dev" else "" + get() = "deltacv EOCV-Sim v" + Build.standardVersionString + if (Build.isDev) "-dev" else "" @JvmField val viewport = SwingOpenCvViewport(Size(1080.0, 720.0), fpsMeterDescriptor) @@ -114,16 +128,6 @@ class Visualizer(val eocvSim: EOCVSim) { private val logger by loggerForThis() fun init(theme: Theme) { - if (Taskbar.isTaskbarSupported()) { - try { - Taskbar.getTaskbar().iconImage = EOCVSimIconLibrary.icoEOCVSim128.image - } catch (_: UnsupportedOperationException) { - logger.warn("Setting the Taskbar icon image is not supported on this platform") - } catch (e: SecurityException) { - logger.error("Security exception while setting TaskBar icon", e) - } - } - try { theme.install() } catch (e: Exception) { @@ -148,16 +152,16 @@ class Visualizer(val eocvSim: EOCVSim) { frame.add(skiaPanel) - menuBar = TopMenuBar(this, eocvSim) + menuBar = TopMenuBar() tunerMenuPanel = JPanel() - sidebarPanel = SidebarPanel(eocvSim) + sidebarPanel = SidebarPanel() - sidebarPipelineTabPanel = SidebarPipelineTabPanel(eocvSim) + sidebarPipelineTabPanel = SidebarPipelineTabPanel() pipelineSelectorPanel = sidebarPipelineTabPanel.pipelineSelectorPanel sourceSelectorPanel = sidebarPipelineTabPanel.sourceSelectorPanel - sidebarOpModeTabPanel = SidebarOpModeTabPanel(eocvSim) + sidebarOpModeTabPanel = SidebarOpModeTabPanel() opModeSelectorPanel = sidebarOpModeTabPanel.opModeSelectorPanel sidebarPanel.add("Pipeline", sidebarPipelineTabPanel) @@ -171,7 +175,7 @@ class Visualizer(val eocvSim: EOCVSim) { frame.jMenuBar = menuBar - frame.contentPane.dropTarget = InputSourceDropTarget(eocvSim) + frame.contentPane.dropTarget = InputSourceDropTarget() tunerCollapsible = CollapsiblePanelX("Variable Tuner", null, null).apply { contentPanel.layout = BoxLayout(contentPanel, BoxLayout.LINE_AXIS) @@ -204,6 +208,17 @@ class Visualizer(val eocvSim: EOCVSim) { EOCVSimIconLibrary.icoEOCVSim16.image ) + if (Taskbar.isTaskbarSupported()) { + val taskbar = Taskbar.getTaskbar() + try { + taskbar.iconImage = EOCVSimIconLibrary.icoEOCVSim128.image + } catch (e: UnsupportedOperationException) { + logger.warn("Setting the Taskbar icon image is not supported on this platform") + } catch (e: SecurityException) { + logger.warn("Setting the Taskbar icon image was not allowed by the security manager") + } + } + frame.setLocationRelativeTo(null) frame.extendedState = JFrame.MAXIMIZED_BOTH frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE @@ -215,8 +230,8 @@ class Visualizer(val eocvSim: EOCVSim) { registerListeners() - eocvSim.inputSourceManager.onInputSourceInitError { - DialogFactory.createInformation( + inputSourceManager.onInputSourceInitError { + dialogFactory.createInformation( frame, "Error while loading requested source", "Falling back to previous source", "Operation failed" @@ -237,19 +252,20 @@ class Visualizer(val eocvSim: EOCVSim) { private fun registerListeners() { frame.addWindowListener(object : WindowAdapter() { override fun windowClosing(e: WindowEvent) { - eocvSim.onMainUpdate.once { eocvSim.destroy() } + onMainUpdate.once { onDestroyRequested.run() } + } }) viewport.component.addMouseListener(object : MouseAdapter() { override fun mouseClicked(e: MouseEvent) { if (!colorPicker.isPicking) { - eocvSim.pipelineManager.callViewportTapped() + pipelineManager.callViewportTapped() } } }) - eocvSim.pipelineManager.onPipelineChange.attach { colorPicker.stopPicking() } + pipelineManager.onPipelineChange.attach { colorPicker.stopPicking() } } fun joinInit() { @@ -262,19 +278,6 @@ class Visualizer(val eocvSim: EOCVSim) { SwingUtilities.invokeLater { frame.isVisible = false viewport.deactivate() - - for (child in childFrames) { - child.isVisible = false - child.dispose() - } - childFrames.clear() - - for (dialog in childDialogs) { - dialog.isVisible = false - dialog.dispose() - } - childDialogs.clear() - frame.dispose() } } @@ -308,7 +311,7 @@ class Visualizer(val eocvSim: EOCVSim) { } fun compilerUnsupported() { - DialogFactory.createInformation( + dialogFactory.createInformation( frame, "Runtime pipeline builds are not supported on this JVM", "For further info, check the EOCV-Sim docs", @@ -316,48 +319,55 @@ class Visualizer(val eocvSim: EOCVSim) { ) } + fun selectPipelinesWorkspace() { - DialogFactory.createFileChooser(frame, DialogFactory.FileChooser.Mode.DIRECTORY_SELECT) + dialogFactory.createFileChooser(frame, DialogFactory.FileChooser.Mode.DIRECTORY_SELECT) .addCloseListener { option, selectedFile, _ -> if (option == JFileChooser.APPROVE_OPTION && selectedFile != null) { if (!selectedFile.exists()) selectedFile.mkdir() - eocvSim.onMainUpdate.once { - eocvSim.workspaceManager.workspaceFile = selectedFile + onMainUpdate.once { + + workspaceManager.workspaceFile = selectedFile } } } } + fun createVSCodeWorkspace() { - DialogFactory.createFileChooser(frame, DialogFactory.FileChooser.Mode.DIRECTORY_SELECT) + dialogFactory.createFileChooser(frame, DialogFactory.FileChooser.Mode.DIRECTORY_SELECT) .addCloseListener { option, selectedFile, _ -> if (option == JFileChooser.APPROVE_OPTION && selectedFile != null) { if (!selectedFile.exists()) selectedFile.mkdir() if (selectedFile.isDirectory && (selectedFile.listFiles()?.size ?: 0) == 0) { - eocvSim.workspaceManager.createWorkspaceWithTemplateAsync(selectedFile, GradleWorkspaceTemplate) { + workspaceManager.createWorkspaceWithTemplateAsync(selectedFile, GradleWorkspaceTemplate) { askOpenVSCode() } } else { - DialogFactory.createInformation( + dialogFactory.createYesOrNo( frame, - "The selected directory must be empty", - "Select an empty directory or create a new one", - "Operation failed" - ) + "Open Workspace", + "The workspace has been successfully created. Do you want to open it?", + ) { result -> + if (result == JOptionPane.YES_OPTION) { + VSCodeLauncher.launch(selectedFile) + } + } } } } } fun askOpenVSCode() { - DialogFactory.createYesOrNo(frame, "A new workspace was created. Do you want to open VS Code?", "") { result -> + 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.asyncLaunch(eocvSim.workspaceManager.workspaceFile, eocvSim.scope) + VSCodeLauncher.asyncLaunch(workspaceManager.workspaceFile, scope) + } } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/FileSelector.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/FileSelector.kt index dae49a39..922b01e7 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/FileSelector.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/FileSelector.kt @@ -25,6 +25,8 @@ package com.github.serivesmejia.eocvsim.gui.component.input import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.util.event.EventHandler +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import java.awt.FlowLayout import java.io.File import javax.swing.* @@ -32,7 +34,9 @@ import javax.swing.filechooser.FileFilter class FileSelector(columns: Int = 18, mode: DialogFactory.FileChooser.Mode, - vararg fileFilters: FileFilter?) : JPanel(FlowLayout()) { + vararg fileFilters: FileFilter?) : JPanel(FlowLayout()), KoinComponent { + + private val dialogFactory: DialogFactory by inject() constructor(columns: Int, vararg fileFilters: FileFilter?) : this(columns, DialogFactory.FileChooser.Mode.FILE_SELECT, *fileFilters) @@ -58,7 +62,7 @@ class FileSelector(columns: Int = 18, selectDirButton.addActionListener { val frame = SwingUtilities.getWindowAncestor(this) - DialogFactory.createFileChooser(frame, mode, "", *fileFilters).addCloseListener { returnVal: Int, selectedFile: File?, selectedFileFilter: FileFilter? -> + dialogFactory.createFileChooser(frame, mode, "", *fileFilters).addCloseListener { returnVal: Int, selectedFile: File?, selectedFileFilter: FileFilter? -> if (returnVal == JFileChooser.APPROVE_OPTION) { lastSelectedFileFilter = selectedFileFilter lastSelectedFile = selectedFile diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.java deleted file mode 100644 index 3fb03727..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.gui.component.tuner; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.gui.component.tuner.element.TunableComboBox; -import com.github.serivesmejia.eocvsim.gui.component.tuner.element.TunableSlider; -import com.github.serivesmejia.eocvsim.gui.component.tuner.element.TunableTextField; -import com.github.serivesmejia.eocvsim.tuner.*; - -import javax.swing.*; -import javax.swing.border.SoftBevelBorder; -import java.awt.*; -import java.util.List; - -@SuppressWarnings("unchecked") -public class TunableFieldPanel extends JPanel { - - public final TunableField tunableField; - - public TunableTextField[] fields; - public JPanel fieldsPanel; - - public TunableSlider[] sliders; - public JPanel slidersPanel; - - public JComboBox[] comboBoxes; - - public TunableFieldPanelOptions panelOptions = null; - private final EOCVSim eocvSim; - - private Mode mode; - private boolean reevalConfigRequested = false; - - private boolean hasBeenShown = false; - - public enum Mode { TEXTBOXES, SLIDERS } - - public TunableFieldPanel(TunableField tunableField, EOCVSim eocvSim) { - super(); - - this.tunableField = tunableField; - this.eocvSim = eocvSim; - - tunableField.setTunableFieldPanel(this); - - init(); - } - - private void init() { - setBorder(new SoftBevelBorder(SoftBevelBorder.RAISED)); - - panelOptions = new TunableFieldPanelOptions(this, eocvSim); - - List> values = tunableField.getTunableValues(); - - boolean hasFieldsOrSliders = false; - for (TunableValue val : values) { - if (val instanceof TunableNumber || val instanceof TunableString) { - hasFieldsOrSliders = true; - break; - } - } - - if(hasFieldsOrSliders) { - add(panelOptions); - } - - JLabel fieldNameLabel = new JLabel(); - fieldNameLabel.setText(tunableField.getFieldName()); - - add(fieldNameLabel); - - fields = new TunableTextField[values.size()]; - sliders = new TunableSlider[values.size()]; - comboBoxes = new JComboBox[values.size()]; - - fieldsPanel = new JPanel(); - slidersPanel = new JPanel(new GridBagLayout()); - - for (int i = 0 ; i < values.size() ; i++) { - TunableValue value = values.get(i); - - if (value instanceof TunableNumber || value instanceof TunableString) { - TunableTextField field = new TunableTextField(value, eocvSim); - fields[i] = field; - field.setEditable(true); - fieldsPanel.add(field); - - if (value instanceof TunableNumber) { - JLabel sliderLabel = new JLabel("0"); - TunableSlider slider = new TunableSlider((TunableNumber) value, eocvSim, sliderLabel); - sliders[i] = slider; - - GridBagConstraints cSlider = new GridBagConstraints(); - cSlider.gridx = 0; - cSlider.gridy = i; - - GridBagConstraints cLabel = new GridBagConstraints(); - cLabel.gridx = 1; - cLabel.gridy = i; - - slidersPanel.add(slider, cSlider); - slidersPanel.add(sliderLabel, cLabel); - } - } else if (value instanceof TunableEnum) { - TunableComboBox comboBox = new TunableComboBox((TunableEnum) value, eocvSim); - add(comboBox); - comboBoxes[i] = comboBox; - } - } - - setMode(Mode.TEXTBOXES); - } - - public void showFieldPanel() { - if(hasBeenShown) return; - hasBeenShown = true; - - panelOptions.getConfigPanel().updateFieldGuiFromConfig(); - if (!tunableField.isOnlyNumbers()) { - setMode(Mode.SLIDERS); - } - } - - public void setFieldValue(int index, Object value) { - if(index >= fields.length) return; - if(fields[index] == null) return; - - if(fields[index].isFocusOwner()) return; - if(sliders[index] != null && sliders[index].isFocusOwner()) return; - - String text; - TunableValue tv = (TunableValue) tunableField.getTunableValues().get(index); - - if(tv instanceof TunableNumber && ((TunableNumber)tv).isOnlyNumbers()) { - text = String.valueOf((int) Math.round(Double.parseDouble(value.toString()))); - } else { - text = value.toString(); - } - - fields[index].setText(text); - - try { - if (sliders[index] != null) sliders[index].setScaledValue(Double.parseDouble(value.toString())); - } catch(NumberFormatException ignored) {} - } - - public void setComboBoxSelection(int index, Object selection) { - if (comboBoxes[index] != null) { - comboBoxes[index].setSelectedItem(selection.toString()); - } - } - - protected void requestAllConfigReeval() { - reevalConfigRequested = true; - } - - public void setMode(Mode mode) { - switch(mode) { - case TEXTBOXES: - if(this.mode == Mode.SLIDERS) { - remove(slidersPanel); - } - - for(int i = 0 ; i < fields.length ; i++) { - if (fields[i] != null) fields[i].setInControl(true); - if (sliders[i] != null) sliders[i].setInControl(false); - if (fields[i] != null) setFieldValue(i, ((TunableValue) tunableField.getTunableValues().get(i)).getValue()); - } - - add(fieldsPanel); - break; - case SLIDERS: - if(this.mode == Mode.TEXTBOXES) { - remove(fieldsPanel); - } - - for(int i = 0 ; i < fields.length ; i++) { - if (fields[i] != null) fields[i].setInControl(false); - if (sliders[i] != null) sliders[i].setInControl(true); - if (fields[i] != null) setFieldValue(i, ((TunableValue) tunableField.getTunableValues().get(i)).getValue()); - } - - add(slidersPanel); - break; - } - - this.mode = mode; - - if(panelOptions.getMode() != mode) { - panelOptions.setMode(mode); - } - - revalidate(); repaint(); - } - - public void setSlidersRange(double min, double max) { - if(sliders == null) return; - for(TunableSlider slider : sliders) { - if (slider != null) slider.setScaledBounds(min, max); - } - } - - public Mode getMode() { return this.mode; } - - public boolean hasRequestedAllConfigReeval() { - boolean current = reevalConfigRequested; - reevalConfigRequested = false; - - return current; - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.kt new file mode 100644 index 00000000..ca22cf6e --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.kt @@ -0,0 +1,199 @@ +package com.github.serivesmejia.eocvsim.gui.component.tuner + + +import com.github.serivesmejia.eocvsim.gui.component.tuner.element.TunableComboBox +import com.github.serivesmejia.eocvsim.gui.component.tuner.element.TunableSlider +import com.github.serivesmejia.eocvsim.gui.component.tuner.element.TunableTextField +import com.github.serivesmejia.eocvsim.tuner.* +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import java.awt.GridBagConstraints +import java.awt.GridBagLayout +import javax.swing.* +import javax.swing.border.SoftBevelBorder + +class TunableFieldPanel(val tunableField: TunableField<*>) : JPanel(), KoinComponent { + + + + var fields: Array? = null + var fieldsPanel: JPanel? = null + + var sliders: Array? = null + var slidersPanel: JPanel? = null + + var comboBoxes: Array?>? = null + + var panelOptions: TunableFieldPanelOptions? = null + + var mode: Mode = Mode.TEXTBOXES + set(value) { + when (value) { + Mode.TEXTBOXES -> { + if (this.mode == Mode.SLIDERS) { + slidersPanel?.let { remove(it) } + } + + fields?.let { + for (i in it.indices) { + it[i]?.isInControl = true + sliders?.get(i)?.inControl = false + it[i]?.let { setFieldValue(i, tunableField.tunableValues[i].value) } + } + } + + fieldsPanel?.let { add(it) } + } + + Mode.SLIDERS -> { + if (this.mode == Mode.TEXTBOXES) { + fieldsPanel?.let { remove(it) } + } + + fields?.let { + for (i in it.indices) { + it[i]?.isInControl = false + sliders?.get(i)?.inControl = true + it[i]?.let { setFieldValue(i, tunableField.tunableValues[i].value) } + } + } + + slidersPanel?.let { add(it) } + } + } + + field = value + + if (panelOptions?.mode != value) { + panelOptions?.mode = field + } + + revalidate() + repaint() + } + + private var reevalConfigRequested = false + private var hasBeenShown = false + + enum class Mode { TEXTBOXES, SLIDERS } + + init { + tunableField.setTunableFieldPanel(this) + initComponents() + } + + private fun initComponents() { + border = SoftBevelBorder(SoftBevelBorder.RAISED) + + val values = tunableField.tunableValues + + fields = arrayOfNulls(values.size) + sliders = arrayOfNulls(values.size) + comboBoxes = arrayOfNulls(values.size) + + fieldsPanel = JPanel() + slidersPanel = JPanel(GridBagLayout()) + + panelOptions = TunableFieldPanelOptions(this) + + val hasFieldsOrSliders = values.any { it is TunableNumber || it is TunableString } + + if (hasFieldsOrSliders) { + add(panelOptions) + } + + val fieldNameLabel = JLabel(tunableField.fieldName) + add(fieldNameLabel) + + for (i in values.indices) { + val value = values[i] + + if (value is TunableNumber || value is TunableString) { + val field = TunableTextField(value) + fields!![i] = field + field.isEditable = true + fieldsPanel!!.add(field) + + if (value is TunableNumber) { + val sliderLabel = JLabel("0") + val slider = TunableSlider(value, sliderLabel) + sliders!![i] = slider + + val cSlider = GridBagConstraints().apply { + gridx = 0 + gridy = i + } + + val cLabel = GridBagConstraints().apply { + gridx = 1 + gridy = i + } + + slidersPanel!!.add(slider, cSlider) + slidersPanel!!.add(sliderLabel, cLabel) + } + } else if (value is TunableEnum<*>) { + val comboBox = TunableComboBox(value) + add(comboBox) + comboBoxes!![i] = comboBox + } + } + + mode = Mode.TEXTBOXES + } + + fun showFieldPanel() { + if (hasBeenShown) return + hasBeenShown = true + + panelOptions?.configPanel?.updateFieldGuiFromConfig() + if (!tunableField.isOnlyNumbers) { + mode = Mode.SLIDERS + } + } + + fun setFieldValue(index: Int, value: Any) { + if (fields == null || index >= fields!!.size) return + val field = fields!![index] ?: return + + if (field.isFocusOwner) return + if (sliders?.get(index)?.isFocusOwner == true) return + + val tv = tunableField.tunableValues[index] + + val text = if (tv is TunableNumber && tv.isOnlyNumbers) { + (value.toString().toDouble().toInt()).toString() + } else { + value.toString() + } + + field.text = text + + try { + sliders?.get(index)?.scaledValue = value.toString().toDouble() + } catch (_: NumberFormatException) { } + } + + fun setComboBoxSelection(index: Int, selection: Any) { + comboBoxes?.get(index)?.selectedItem = selection.toString() + } + + fun requestAllConfigReeval() { + reevalConfigRequested = true + } + + fun setSlidersRange(min: Double, max: Double) { + sliders?.let { + for (slider in it) { + slider?.setScaledBounds(min, max) + } + } + } + + fun hasRequestedAllConfigReeval(): Boolean { + val current = reevalConfigRequested + reevalConfigRequested = false + return current + } + +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelConfig.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelConfig.kt index 52968d0d..92cbd747 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelConfig.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelConfig.kt @@ -23,11 +23,14 @@ package com.github.serivesmejia.eocvsim.gui.component.tuner -import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.gui.component.PopupX import com.github.serivesmejia.eocvsim.gui.component.input.EnumComboBox import com.github.serivesmejia.eocvsim.gui.component.input.SizeFields import com.github.serivesmejia.eocvsim.tuner.TunableField +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import io.github.deltacv.eocvsim.plugin.loader.PluginManager import kotlinx.coroutines.* import kotlinx.coroutines.swing.Swing import org.opencv.core.Size @@ -38,11 +41,20 @@ import java.awt.GridBagLayout import javax.swing.* import javax.swing.border.EmptyBorder +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named + @OptIn(DelicateCoroutinesApi::class) -class TunableFieldPanelConfig(private val fieldOptions: TunableFieldPanelOptions, - private val eocvSim: EOCVSim) : JPanel() { +class TunableFieldPanelConfig(private val fieldOptions: TunableFieldPanelOptions) : JPanel(), KoinComponent { + + private val configManager: ConfigManager by inject() + private val scope: CoroutineScope by inject() + + + + var localConfig = configManager.config.globalTunableFieldsConfig.copy() - var localConfig = eocvSim.config.globalTunableFieldsConfig.copy() private set private var lastApplyPopup: PopupX? = null @@ -159,16 +171,17 @@ class TunableFieldPanelConfig(private val fieldOptions: TunableFieldPanelOptions it.closeOnFocusLost = true //launch the waiting in the background - eocvSim.scope.launch { - delay(100) - //close config popup if still hasn't focused after a bit - launch(Dispatchers.Swing) { - if (!it.window.isFocused && (lastApplyPopup == null || lastApplyPopup?.window?.isFocused == false)) { - it.hide() - } + scope.launch { + delay(100) + //close config popup if still hasn't focused after a bit + launch(Dispatchers.Swing) { + if (!it.window.isFocused && (lastApplyPopup == null || lastApplyPopup?.window?.isFocused == false)) { + it.hide() } } } + + } } popup.show() @@ -232,7 +245,8 @@ class TunableFieldPanelConfig(private val fieldOptions: TunableFieldPanelOptions applyToConfig() //saves the current values to the current local config localConfig.source = ConfigSource.GLOBAL //changes the source of the local config to global - eocvSim.config.globalTunableFieldsConfig = localConfig.copy() + configManager.config.globalTunableFieldsConfig = localConfig.copy() + updateConfigSourceLabel() fieldOptions.fieldPanel.requestAllConfigReeval() @@ -244,7 +258,8 @@ class TunableFieldPanelConfig(private val fieldOptions: TunableFieldPanelOptions val typeClass = fieldOptions.fieldPanel.tunableField::class.java localConfig.source = ConfigSource.TYPE_SPECIFIC //changes the source of the local config to type specific - eocvSim.config.specificTunableFieldConfig[typeClass.name] = localConfig.copy() + configManager.config.specificTunableFieldConfig[typeClass.name] = localConfig.copy() + updateConfigSourceLabel() fieldOptions.fieldPanel.requestAllConfigReeval() @@ -252,19 +267,20 @@ class TunableFieldPanelConfig(private val fieldOptions: TunableFieldPanelOptions //loads the config from global eocv sim config file internal fun applyFromEOCVSimConfig() { - val specificConfigs = eocvSim.config.specificTunableFieldConfig + val specificConfigs = configManager.config.specificTunableFieldConfig //apply specific config if we have one, or else, apply global localConfig = if(specificConfigs.containsKey(fieldTypeClass.name)) { specificConfigs[fieldTypeClass.name]!!.copy() } else { - eocvSim.config.globalTunableFieldsConfig.copy() + configManager.config.globalTunableFieldsConfig.copy() } updateConfigGuiFromConfig() updateConfigSourceLabel() } + //applies the current values to the specified config, defaults to local @Suppress("UNNECESSARY_SAFE_CALL") private fun applyToConfig(config: Config = localConfig) { @@ -282,9 +298,7 @@ class TunableFieldPanelConfig(private val fieldOptions: TunableFieldPanelOptions } //sets the panel mode (sliders or textboxes) to config from the current mode - if(fieldOptions.fieldPanel?.mode != null) { - config.fieldPanelMode = fieldOptions.fieldPanel.mode - } + config.fieldPanelMode = fieldOptions.fieldPanel.mode } private fun updateConfigSourceLabel(currentConfig: Config = localConfig) { @@ -306,7 +320,7 @@ class TunableFieldPanelConfig(private val fieldOptions: TunableFieldPanelOptions //sets the slider range from config fieldOptions.fieldPanel.setSlidersRange(localConfig.sliderRange.width, localConfig.sliderRange.height) //sets the panel mode (sliders or textboxes) to config from the current mode - if(fieldOptions.fieldPanel?.fields != null){ + if(fieldOptions.fieldPanel.fields != null){ fieldOptions.fieldPanel.mode = localConfig.fieldPanelMode } } 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 94acc1d2..92e8f908 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 @@ -23,9 +23,10 @@ package com.github.serivesmejia.eocvsim.gui.component.tuner -import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary import com.github.serivesmejia.eocvsim.gui.component.PopupX +import com.github.serivesmejia.eocvsim.tuner.TunableNumber import io.github.deltacv.vision.external.util.extension.cvtColor import com.github.serivesmejia.eocvsim.util.extension.clipUpperZero import java.awt.FlowLayout @@ -36,8 +37,13 @@ import javax.swing.* import javax.swing.event.AncestorEvent import javax.swing.event.AncestorListener -class TunableFieldPanelOptions(val fieldPanel: TunableFieldPanel, - eocvSim: EOCVSim) : JPanel() { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class TunableFieldPanelOptions(val fieldPanel: TunableFieldPanel) : JPanel(), KoinComponent { + + val visualizer: Visualizer by inject() + private val sliderIco by EOCVSimIconLibrary.icoSlider.lazyResized(15, 15) private val textBoxIco by EOCVSimIconLibrary.icoTextbox.lazyResized(15, 15) @@ -48,7 +54,8 @@ class TunableFieldPanelOptions(val fieldPanel: TunableFieldPanel, private val configButton = JButton() private val colorPickButton = JToggleButton() - val configPanel = TunableFieldPanelConfig(this, eocvSim) + val configPanel = TunableFieldPanelConfig(this) + var lastConfigPopup: PopupX? = null private set @@ -114,7 +121,8 @@ class TunableFieldPanelOptions(val fieldPanel: TunableFieldPanel, } colorPickButton.addActionListener { - val colorPicker = eocvSim.visualizer.colorPicker + val colorPicker = visualizer.colorPicker + //start picking if global color picker is not being used by other panel if(!colorPicker.isPicking && colorPickButton.isSelected) { @@ -144,16 +152,21 @@ class TunableFieldPanelOptions(val fieldPanel: TunableFieldPanel, colorPicker.onPick.once { val colorScalar = colorPicker.colorRgb.cvtColor(configPanel.localConfig.pickerColorSpace.cvtCode) - //setting the scalar value in order from first to fourth field - for(i in 0..(fieldPanel.fields.size - 1).clipUpperZero()) { - //if we're still in range of the scalar values amount - if(i < colorScalar.`val`.size) { - val colorVal = colorScalar.`val`[i] - - val tv = fieldPanel.tunableField.tunableValues.getOrNull(i) as? com.github.serivesmejia.eocvsim.tuner.TunableNumber - tv?.setFromGui(colorVal) - } else { break } //keep looping until we write the entire scalar value + fieldPanel.fields?.let { + //setting the scalar value in order from first to fourth field + for (i in 0..(it.size - 1).clipUpperZero()) { + //if we're still in range of the scalar values amount + if (i < colorScalar.`val`.size) { + val colorVal = colorScalar.`val`[i] + + val tv = fieldPanel.tunableField.tunableValues.getOrNull(i) as? TunableNumber + tv?.setFromGui(colorVal) + } else { + break + } //keep looping until we write the entire scalar value + } } + colorPickButton.isSelected = false } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableComboBox.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableComboBox.java deleted file mode 100644 index 36f23fe4..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableComboBox.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.gui.component.tuner.element; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.tuner.TunableEnum; -import com.github.serivesmejia.eocvsim.tuner.TunableField; - -import javax.swing.*; -import java.util.Objects; - -@SuppressWarnings({"unchecked", "rawtypes"}) -public class TunableComboBox extends JComboBox { - - private final TunableEnum tunableValue; - - private final EOCVSim eocvSim; - - public TunableComboBox(TunableEnum tunableValue, EOCVSim eocvSim) { - super(); - - this.tunableValue = tunableValue; - this.eocvSim = eocvSim; - - init(); - } - - private void init() { - for (Object obj : tunableValue.getEnumValues()) { - this.addItem(obj.toString()); - } - - addItemListener(evt -> eocvSim.onMainUpdate.once(() -> { - if (evt.getStateChange() == java.awt.event.ItemEvent.SELECTED) { - Object[] values = tunableValue.getEnumValues(); - Object selected = null; - String selectedStr = Objects.requireNonNull(getSelectedItem()).toString(); - - for (Object val : values) { - if (val.toString().equals(selectedStr)) { - selected = val; - break; - } - } - - if (selected != null) { - ((TunableEnum) tunableValue).setFromGui((Enum) selected); - } - - if (eocvSim.pipelineManager.getPaused()) { - eocvSim.pipelineManager.requestSetPaused(false); - } - } - })); - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableComboBox.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableComboBox.kt new file mode 100644 index 00000000..a1d40a14 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableComboBox.kt @@ -0,0 +1,55 @@ +package com.github.serivesmejia.eocvsim.gui.component.tuner.element + +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.tuner.TunableEnum +import org.koin.core.qualifier.named +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import java.awt.event.ItemEvent +import javax.swing.JComboBox + +class TunableComboBox(val tunableValue: TunableEnum<*>) : JComboBox(), KoinComponent { + + private val pipelineManager: PipelineManager by inject() + private val onMainUpdate: EventHandler by inject(named("onMainLoop")) + + + init { + initComponents() + } + + private fun initComponents() { + for (obj in tunableValue.enumValues) { + addItem(obj.toString()) + } + + addItemListener { evt -> + onMainUpdate.once { + if (evt.stateChange == ItemEvent.SELECTED) { + val values = tunableValue.enumValues + var selected: Any? = null + val selectedStr = selectedItem?.toString() ?: return@once + + for (valObj in values) { + if (valObj.toString() == selectedStr) { + selected = valObj + break + } + } + + if (selected != null) { + @Suppress("UNCHECKED_CAST") + (tunableValue as TunableEnum>).setFromGui(selected as Enum<*>) + } + + if (pipelineManager.paused) { + pipelineManager.requestSetPaused(false) + } + } + } + } + + } + +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableSlider.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableSlider.kt index 4b640b55..7b69df70 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableSlider.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableSlider.kt @@ -23,35 +23,45 @@ package com.github.serivesmejia.eocvsim.gui.component.tuner.element -import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.gui.component.SliderX +import org.koin.core.qualifier.named import com.github.serivesmejia.eocvsim.tuner.TunableNumber import javax.swing.JLabel import kotlin.math.roundToInt +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + class TunableSlider(val tunableValue: TunableNumber, - val eocvSim: EOCVSim, val valueLabel: JLabel? = null, minBound: Double = 0.0, - maxBound: Double = 255.0) : SliderX(minBound, maxBound, 10) { + maxBound: Double = 255.0) : SliderX(minBound, maxBound, 10), KoinComponent { + + private val pipelineManager: PipelineManager by inject() + private val onMainUpdate: EventHandler by inject(named("onMainLoop")) + + var inControl = false - constructor(tunableValue: TunableNumber, eocvSim: EOCVSim, valueLabel: JLabel) : this(tunableValue, eocvSim, valueLabel, 0.0, 255.0) + constructor(tunableValue: TunableNumber, valueLabel: JLabel) : this(tunableValue, valueLabel, 0.0, 255.0) - constructor(tunableValue: TunableNumber, eocvSim: EOCVSim) : this(tunableValue, eocvSim, null, 0.0, 255.0) + constructor(tunableValue: TunableNumber) : this(tunableValue, null, 0.0, 255.0) init { addChangeListener { - eocvSim.onMainUpdate.once { + onMainUpdate.once { if(inControl) { tunableValue.setFromGui(scaledValue) - if (eocvSim.pipelineManager.paused) - eocvSim.pipelineManager.setPaused(false) + if (pipelineManager.paused) + pipelineManager.setPaused(false) } } + valueLabel?.text = if (!tunableValue.isOnlyNumbers) { scaledValue.toString() } else { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.java deleted file mode 100644 index b103d14e..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.gui.component.tuner.element; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.tuner.TunableNumber; -import com.github.serivesmejia.eocvsim.tuner.TunableString; -import com.github.serivesmejia.eocvsim.tuner.TunableValue; - -import javax.swing.*; -import javax.swing.border.Border; -import javax.swing.border.LineBorder; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.text.AbstractDocument; -import javax.swing.text.AttributeSet; -import javax.swing.text.BadLocationException; -import javax.swing.text.DocumentFilter; -import java.awt.*; -import java.awt.event.KeyEvent; -import java.awt.event.KeyListener; -import java.util.ArrayList; -import java.util.Collections; - -public class TunableTextField extends JTextField { - - private final ArrayList validCharsIfNumber = new ArrayList<>(); - - private final TunableValue tunableValue; - private final EOCVSim eocvSim; - - private final Border initialBorder; - - private volatile boolean hasValidText = true; - - private boolean inControl = false; - - public TunableTextField(TunableValue tunableValue, EOCVSim eocvSim) { - super(); - - this.initialBorder = this.getBorder(); - - this.tunableValue = tunableValue; - this.eocvSim = eocvSim; - - setText(tunableValue.getValue().toString()); - - int plusW = Math.round(getText().length() / 5f) * 10; - this.setPreferredSize(new Dimension(40 + plusW, getPreferredSize().height)); - - tunableValue.getOnValueChange().attach(() -> { - if(!inControl) { - setText(tunableValue.getValue().toString()); - } - }); - - boolean isNumber = tunableValue instanceof TunableNumber; - - if (isNumber) { - TunableNumber numValue = (TunableNumber) tunableValue; - - //add all valid characters for non decimal numeric fields - Collections.addAll(validCharsIfNumber, '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-'); - - //allow dots for decimal numeric fields - if (!numValue.isOnlyNumbers()) { - validCharsIfNumber.add('.'); - } - - ((AbstractDocument) getDocument()).setDocumentFilter(new DocumentFilter() { - - @Override - public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException { - text = text.replace(" ", ""); - - for (char c : text.toCharArray()) { - if (!isNumberCharacter(c)) return; - } - - boolean invalidNumber = false; - - try { //check if entered text is valid number - Double.valueOf(text); - } catch (NumberFormatException ex) { - invalidNumber = true; - } - - hasValidText = !invalidNumber || !text.isEmpty(); - - if (hasValidText) { - setNormalBorder(); - } else { - setRedBorder(); - } - - super.replace(fb, offset, length, text, attrs); - } - - }); - - } - - getDocument().addDocumentListener(new DocumentListener() { - - Runnable changeFieldValue = () -> { - String text = getText(); - - if ((!hasValidText || !isNumber || (text != null && !getText().trim().equals("")))) { - try { - if (isNumber) { - ((TunableNumber) tunableValue).setFromGui(Double.parseDouble(text)); - } else if (tunableValue instanceof TunableString) { - ((TunableString) tunableValue).setFromGui(text); - } - } catch (Exception e) { - setRedBorder(); - } - } else { - setRedBorder(); - } - }; - - @Override - public void insertUpdate(DocumentEvent e) { - change(); - } - @Override - public void removeUpdate(DocumentEvent e) { change(); } - @Override - public void changedUpdate(DocumentEvent e) { change(); } - - public void change() { - eocvSim.onMainUpdate.once(changeFieldValue); - } - }); - - //unpausing when typing on any tunable text box - addKeyListener(new KeyListener() { - @Override - public void keyTyped(KeyEvent e) { - execute(); - } - @Override - public void keyPressed(KeyEvent e) { - execute(); - } - @Override - public void keyReleased(KeyEvent e) { execute(); } - - public void execute() { - if (eocvSim.pipelineManager.getPaused()) { - eocvSim.pipelineManager.requestSetPaused(false); - } - } - }); - } - - public void setNormalBorder() { - setBorder(initialBorder); - } - - public void setRedBorder() { - setBorder(new LineBorder(new Color(255, 79, 79), 2)); - } - - public void setInControl(boolean inControl) { - this.inControl = inControl; - } - - private boolean isNumberCharacter(char c) { - for (char validC : validCharsIfNumber) { - if (c == validC) return true; - } - return false; - } - - public boolean isInControl() { return inControl; } - -} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.kt new file mode 100644 index 00000000..2c856ef8 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.kt @@ -0,0 +1,154 @@ +package com.github.serivesmejia.eocvsim.gui.component.tuner.element + +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.tuner.TunableNumber +import com.github.serivesmejia.eocvsim.tuner.TunableString +import com.github.serivesmejia.eocvsim.tuner.TunableValue +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import org.koin.core.qualifier.named +import javax.swing.JTextField +import javax.swing.border.Border +import javax.swing.border.LineBorder +import javax.swing.event.DocumentEvent +import javax.swing.event.DocumentListener +import javax.swing.text.AbstractDocument +import javax.swing.text.AttributeSet +import javax.swing.text.BadLocationException +import javax.swing.text.DocumentFilter +import java.awt.Color +import java.awt.Dimension +import java.awt.event.KeyEvent +import java.awt.event.KeyListener + +class TunableTextField(val tunableValue: TunableValue<*>) : JTextField(), KoinComponent { + + private val pipelineManager: PipelineManager by inject() + private val onMainUpdate: EventHandler by inject(named("onMainLoop")) + + + private val validCharsIfNumber = mutableListOf() + private val initialBorder: Border = this.border + @Volatile private var hasValidText = true + var isInControl = false + + init { + text = tunableValue.value.toString() + + val plusW = (text.length / 5f).toInt() * 10 + preferredSize = Dimension(40 + plusW, preferredSize.height) + + tunableValue.onValueChange.attach { + if (!isInControl) { + text = tunableValue.value.toString() + } + } + + val isNumber = tunableValue is TunableNumber + + if (isNumber) { + val numValue = tunableValue + + //add all valid characters for non decimal numeric fields + validCharsIfNumber.addAll(listOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-')) + + //allow dots for decimal numeric fields + if (!numValue.isOnlyNumbers) { + validCharsIfNumber.add('.') + } + + (document as AbstractDocument).documentFilter = object : DocumentFilter() { + @Throws(BadLocationException::class) + override fun replace( + fb: FilterBypass, + offset: Int, + length: Int, + text: String, + attrs: AttributeSet? + ) { + val filteredText = text.replace(" ".toRegex(), "") + + for (c in filteredText.toCharArray()) { + if (!isNumberCharacter(c)) return + } + + var invalidNumber = false + + try { //check if entered text is valid number + filteredText.toDouble() + } catch (ex: NumberFormatException) { + invalidNumber = true + } + + hasValidText = !invalidNumber || filteredText.isNotEmpty() + + if (hasValidText) { + setNormalBorder() + } else { + setRedBorder() + } + + super.replace(fb, offset, length, filteredText, attrs) + } + } + } + + document.addDocumentListener(object : DocumentListener { + val changeFieldValue = Runnable { + val currentText = text + + if (!hasValidText || !isNumber || (currentText != null && currentText.trim().isNotEmpty())) { + try { + if (isNumber) { + tunableValue.setFromGui(currentText.toDouble()) + } else if (tunableValue is TunableString) { + tunableValue.setFromGui(currentText) + } + } catch (e: Exception) { + setRedBorder() + } + } else { + setRedBorder() + } + } + + override fun insertUpdate(e: DocumentEvent) = change() + override fun removeUpdate(e: DocumentEvent) = change() + override fun changedUpdate(e: DocumentEvent) = change() + + private fun change() { + onMainUpdate.once(changeFieldValue) + + } + }) + + //unpausing when typing on any tunable text box + addKeyListener(object : KeyListener { + override fun keyTyped(e: KeyEvent) = execute() + override fun keyPressed(e: KeyEvent) = execute() + override fun keyReleased(e: KeyEvent) = execute() + + private fun execute() { + if (pipelineManager.paused) { + pipelineManager.requestSetPaused(false) + } + + } + }) + } + + fun setNormalBorder() { + border = initialBorder + } + + fun setRedBorder() { + border = LineBorder(Color(255, 79, 79), 2) + } + + private fun isNumberCharacter(c: Char): Boolean { + return validCharsIfNumber.contains(c) + } + +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/CreateSourcePanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/CreateSourcePanel.kt index 68a379e4..dda4761d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/CreateSourcePanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/CreateSourcePanel.kt @@ -28,12 +28,16 @@ import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.component.PopupX import com.github.serivesmejia.eocvsim.gui.component.input.EnumComboBox import com.github.serivesmejia.eocvsim.input.SourceType +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import java.awt.FlowLayout import java.awt.GridLayout import javax.swing.JButton import javax.swing.JPanel -class CreateSourcePanel(eocvSim: EOCVSim) : JPanel(GridLayout(2, 1)) { +class CreateSourcePanel(eocvSim: EOCVSim) : JPanel(GridLayout(2, 1)), KoinComponent { + + private val dialogFactory: DialogFactory by inject() private val sourceSelectComboBox = EnumComboBox( "", SourceType::class.java, SourceType.values(), @@ -54,7 +58,7 @@ class CreateSourcePanel(eocvSim: EOCVSim) : JPanel(GridLayout(2, 1)) { nextButton.addActionListener { //creates "create source" dialog from selected enum - DialogFactory.createSourceDialog(eocvSim, sourceSelectComboBox.selectedEnum!!) + dialogFactory.createSourceDialog(sourceSelectComboBox.selectedEnum!!) popup?.hide() } nextPanel.add(nextButton) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/InputSourceDropTarget.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/InputSourceDropTarget.kt index c8c541ff..0df9cf6b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/InputSourceDropTarget.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/InputSourceDropTarget.kt @@ -27,16 +27,20 @@ import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.input.SourceType import io.github.deltacv.common.util.loggerForThis +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import java.awt.datatransfer.DataFlavor import java.awt.dnd.DnDConstants import java.awt.dnd.DropTarget import java.awt.dnd.DropTargetDropEvent import java.io.File -class InputSourceDropTarget(val eocvSim: EOCVSim) : DropTarget() { +class InputSourceDropTarget : DropTarget(), KoinComponent { val logger by loggerForThis() + private val dialogFactory: DialogFactory by inject() + @Suppress("UNCHECKED_CAST") override fun drop(evt: DropTargetDropEvent) { try { @@ -49,7 +53,7 @@ class InputSourceDropTarget(val eocvSim: EOCVSim) : DropTarget() { val sourceType = SourceType.isFileUsableForSource(file) if(sourceType != SourceType.UNKNOWN) { - DialogFactory.createSourceDialog(eocvSim, sourceType, file) + dialogFactory.createSourceDialog(sourceType, file) break } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/SidebarPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/SidebarPanel.kt index 916d1655..b7f53829 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/SidebarPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/SidebarPanel.kt @@ -23,7 +23,7 @@ package com.github.serivesmejia.eocvsim.gui.component.visualizer -import com.github.serivesmejia.eocvsim.EOCVSim + import com.github.serivesmejia.eocvsim.util.event.EventHandler import io.github.deltacv.common.util.loggerForThis import java.awt.Font @@ -31,7 +31,13 @@ import java.awt.LayoutManager import javax.swing.JPanel import javax.swing.JTabbedPane -class SidebarPanel(val eocvSim: EOCVSim) : JTabbedPane() { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class SidebarPanel : JTabbedPane(), KoinComponent { + + + private var previousActiveIndex = -1; diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/TelemetryPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/TelemetryPanel.kt index 9011ce03..f9436a14 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/TelemetryPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/TelemetryPanel.kt @@ -38,7 +38,13 @@ import javax.swing.* import javax.swing.border.EmptyBorder import javax.swing.border.TitledBorder -class TelemetryPanel(pipelineManager: PipelineManager? = null) : JPanel(), TelemetryTransmissionReceiver { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class TelemetryPanel : JPanel(), TelemetryTransmissionReceiver, KoinComponent { + + private val pipelineManager: PipelineManager by inject() + val telemetryScroll = JScrollPane() val telemetryList = JList() @@ -95,14 +101,13 @@ class TelemetryPanel(pipelineManager: PipelineManager? = null) : JPanel(), Telem ipady = 20 }) - if(pipelineManager != null) { - pipelineManager.onPipelineChange { // update telemetry receiver on pipeline change - val telemetry = pipelineManager.currentTelemetry - if (telemetry is EOCVSimTelemetryImpl) { - telemetry.addTransmissionReceiver(this@TelemetryPanel) - } + pipelineManager.onPipelineChange { // update telemetry receiver on pipeline change + val telemetry = pipelineManager.currentTelemetry + if (telemetry is EOCVSimTelemetryImpl) { + telemetry.addTransmissionReceiver(this@TelemetryPanel) } } + } fun revalAndRepaint() { 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 6b2fadab..7303d128 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 @@ -44,7 +44,29 @@ import javax.swing.JMenu import javax.swing.JMenuBar import javax.swing.JMenuItem -class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { +import io.github.deltacv.eocvsim.plugin.loader.PluginManager +import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import org.koin.core.qualifier.named +import kotlinx.coroutines.CoroutineScope +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class TopMenuBar : JMenuBar(), KoinComponent { + + val visualizer: Visualizer by inject() + val dialogFactory: DialogFactory by inject() + val pluginManager: PluginManager by inject() + val workspaceManager: WorkspaceManager by inject() + val pipelineManager: PipelineManager by inject() + val onMainUpdate: EventHandler by inject(named("onMainLoop")) + val onRestartRequested: EventHandler by inject(named("onRestartRequested")) + val scope: CoroutineScope by inject() + + + + companion object { val docsUrl = URI("https://docs.deltacv.org/eocv-sim/") @@ -72,7 +94,7 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { val fileNewInputSourceItem = JMenuItem(type.coolName) fileNewInputSourceItem.addActionListener { - DialogFactory.createSourceDialog(eocvSim, type) + dialogFactory.createSourceDialog(type) } fileNewInputSourceSubmenu.add(fileNewInputSourceItem) @@ -88,7 +110,7 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { GuiUtil.saveMatFileChooser( visualizer.frame, mat, - eocvSim + dialogFactory ) mat.release() @@ -98,15 +120,16 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { mFileMenu.addSeparator() if (desktop.isSupported(Desktop.Action.APP_PREFERENCES)) { - desktop.setPreferencesHandler { DialogFactory.createConfigDialog(eocvSim) } + desktop.setPreferencesHandler { dialogFactory.createConfigDialog() } } else { val editSettings = JMenuItem("Settings") - editSettings.addActionListener { DialogFactory.createConfigDialog(eocvSim) } + editSettings.addActionListener { dialogFactory.createConfigDialog() } mFileMenu.add(editSettings) } val filePlugins = JMenuItem("Manage Plugins") - filePlugins.addActionListener { eocvSim.pluginManager.appender.append(PluginOutput.SPECIAL_OPEN_MGR)} + filePlugins.addActionListener { pluginManager.appender.append(PluginOutput.SPECIAL_OPEN_MGR)} + mFileMenu.add(filePlugins) @@ -114,7 +137,8 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { val fileRestart = JMenuItem("Restart") - fileRestart.addActionListener { eocvSim.onMainUpdate.once { eocvSim.restart() } } + fileRestart.addActionListener { onMainUpdate.once { onRestartRequested.run() } } + mFileMenu.add(fileRestart) add(mFileMenu) @@ -123,28 +147,30 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { val workspSetWorkspace = JMenuItem("Select Workspace") - workspSetWorkspace.addActionListener { DialogFactory.createWorkspace(visualizer) } + workspSetWorkspace.addActionListener { dialogFactory.createWorkspace() } mWorkspMenu.add(workspSetWorkspace) val workspClose = JMenuItem("Close Current Workspace") workspClose.addActionListener { - eocvSim.onMainUpdate.once { - eocvSim.workspaceManager.workspaceFile = CompiledPipelineManager.DEF_WORKSPACE_FOLDER + onMainUpdate.once { + workspaceManager.workspaceFile = CompiledPipelineManager.DEF_WORKSPACE_FOLDER } } + mWorkspMenu.add(workspClose) mWorkspMenu.addSeparator() - workspCompile.addActionListener { eocvSim.pipelineManager.compiledPipelineManager.asyncBuild() } + workspCompile.addActionListener { pipelineManager.compiledPipelineManager.asyncBuild() } + mWorkspMenu.add(workspCompile) val workspBuildOutput = JMenuItem("Output") workspBuildOutput.addActionListener { if(!Output.isAlreadyOpened) - DialogFactory.createOutput(eocvSim, true) + dialogFactory.createOutput(true) } mWorkspMenu.add(workspBuildOutput) @@ -162,7 +188,8 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { val workspVSCodeOpen = JMenuItem("Open VS Code Here") workspVSCodeOpen.addActionListener { - VSCodeLauncher.asyncLaunch(eocvSim.workspaceManager.workspaceFile, eocvSim.scope) + VSCodeLauncher.asyncLaunch(workspaceManager.workspaceFile, scope) + } workspVSCode.add(workspVSCodeOpen) @@ -192,7 +219,7 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { crashReport = CrashReport(e, isDummy = true) } - DialogFactory.createFileChooser(visualizer.frame, + dialogFactory.createFileChooser(visualizer.frame, DialogFactory.FileChooser.Mode.SAVE_FILE_SELECT, CrashReport.defaultCrashFileName, FileFilters.logFileFilter ).addCloseListener { OPTION, selectedFile, _ -> @@ -215,16 +242,16 @@ class TopMenuBar(visualizer: Visualizer, eocvSim: EOCVSim) : JMenuBar() { mHelpMenu.addSeparator() val helpIAmA = JMenuItem("I am a...") - helpIAmA.addActionListener { DialogFactory.createIAmA(eocvSim.visualizer) } + helpIAmA.addActionListener { dialogFactory.createIAmA() } mHelpMenu.add(helpIAmA) if (desktop.isSupported(Desktop.Action.APP_ABOUT)) { - desktop.setAboutHandler { DialogFactory.createAboutDialog(eocvSim) } + desktop.setAboutHandler { dialogFactory.createAboutDialog() } } else { val helpAbout = JMenuItem("About") - helpAbout.addActionListener { DialogFactory.createAboutDialog(eocvSim) } + helpAbout.addActionListener { dialogFactory.createAboutDialog() } mHelpMenu.add(helpAbout) } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeControlsPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeControlsPanel.kt index 3387877d..411e4e0b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeControlsPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeControlsPanel.kt @@ -23,7 +23,6 @@ package com.github.serivesmejia.eocvsim.gui.component.visualizer.opmode -import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.qualcomm.robotcore.eventloop.opmode.OpMode @@ -34,7 +33,14 @@ import javax.swing.JPanel import javax.swing.JButton import javax.swing.SwingUtilities -class OpModeControlsPanel(val eocvSim: EOCVSim) : JPanel() { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class OpModeControlsPanel : JPanel(), KoinComponent { + + private val pipelineManager: PipelineManager by inject() + + val controlButton = JButton() @@ -55,12 +61,12 @@ class OpModeControlsPanel(val eocvSim: EOCVSim) : JPanel() { controlButton.icon = EOCVSimIconLibrary.icoFlag controlButton.addActionListener { - eocvSim.pipelineManager.onUpdate.once { - if(eocvSim.pipelineManager.currentPipeline !is OpMode) return@once + pipelineManager.onUpdate.once { + if(pipelineManager.currentPipeline !is OpMode) return@once - eocvSim.pipelineManager.setPaused(false, PipelineManager.PauseReason.NOT_PAUSED) + pipelineManager.setPaused(false, PipelineManager.PauseReason.NOT_PAUSED) - val opMode = eocvSim.pipelineManager.currentPipeline as OpMode + val opMode = pipelineManager.currentPipeline as OpMode val state = opMode.notifier.state opMode.notifier.notify(when(state) { @@ -71,18 +77,21 @@ class OpModeControlsPanel(val eocvSim: EOCVSim) : JPanel() { }) } } + } fun stopCurrentOpMode() { - if(eocvSim.pipelineManager.currentPipeline != currentOpMode || currentOpMode == null) return + if(pipelineManager.currentPipeline != currentOpMode || currentOpMode == null) return + currentOpMode!!.notifier.notify(OpModeNotification.STOP) } private fun notifySelected() { if(!isActive) return - if(eocvSim.pipelineManager.currentPipeline !is OpMode) return - val opMode = eocvSim.pipelineManager.currentPipeline as OpMode + if(pipelineManager.currentPipeline !is OpMode) return + val opMode = pipelineManager.currentPipeline as OpMode + val opModeIndex = currentManagerIndex!! opMode.notifier.onStateChange { @@ -123,20 +132,21 @@ class OpModeControlsPanel(val eocvSim: EOCVSim) : JPanel() { } fun opModeSelected(managerIndex: Int, forceChangePipeline: Boolean = true) { - eocvSim.pipelineManager.requestSetPaused(false) + pipelineManager.requestSetPaused(false) if(forceChangePipeline) { - eocvSim.pipelineManager.requestForceChangePipeline(managerIndex) + pipelineManager.requestForceChangePipeline(managerIndex) } upcomingIndex = managerIndex - eocvSim.pipelineManager.onUpdate.once { + pipelineManager.onUpdate.once { currentManagerIndex = managerIndex notifySelected() } } + fun reset() { controlButton.isEnabled = false controlButton.icon = EOCVSimIconLibrary.icoFlag diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeSelectorPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeSelectorPanel.kt index 79807d2f..13df6867 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeSelectorPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeSelectorPanel.kt @@ -23,7 +23,7 @@ package com.github.serivesmejia.eocvsim.gui.component.visualizer.opmode -import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary import com.github.serivesmejia.eocvsim.gui.component.PopupX.Companion.popUpXOnThis import com.github.serivesmejia.eocvsim.gui.util.Corner @@ -35,15 +35,22 @@ import com.qualcomm.robotcore.eventloop.opmode.* import com.qualcomm.robotcore.util.Range import io.github.deltacv.vision.internal.opmode.OpModeState import java.awt.GridBagConstraints -import java.awt.GridBagLayout -import java.awt.event.MouseAdapter import java.awt.event.MouseEvent import javax.swing.* +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import java.awt.GridBagLayout +import java.awt.event.MouseAdapter + +class OpModeSelectorPanel(val opModeControlsPanel: OpModeControlsPanel) : JPanel(), KoinComponent { + + private val pipelineManager: PipelineManager by inject() + -class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeControlsPanel) : JPanel() { private var _selectedIndex = -1 + private val logger by loggerForThis() var selectedIndex: Int @@ -93,8 +100,9 @@ class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeC autonomousSelector.selectionMode = ListSelectionModel.SINGLE_SELECTION teleopSelector.selectionMode = ListSelectionModel.SINGLE_SELECTION - autonomousSelector.cellRenderer = PipelineListIconRenderer(eocvSim.pipelineManager) { autonomousIndexMap } - teleopSelector.cellRenderer = PipelineListIconRenderer(eocvSim.pipelineManager) { teleopIndexMap } + autonomousSelector.cellRenderer = PipelineListIconRenderer(pipelineManager) { autonomousIndexMap } + teleopSelector.cellRenderer = PipelineListIconRenderer(pipelineManager) { teleopIndexMap } + autonomousButton.icon = EOCVSimIconLibrary.icoArrowDropdown @@ -216,7 +224,7 @@ class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeC } }) - eocvSim.pipelineManager.onPipelineChange { + pipelineManager.onPipelineChange { if (!isActive) return@onPipelineChange // we are doing this to detect external pipeline changes and reflect them @@ -226,9 +234,9 @@ class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeC // we need to hold on a cycle so that the state has been fully updated, // just to be able to check correctly and, if it was requested by // OpModeSelectorPanel, skip this message and not do anything. - eocvSim.pipelineManager.onUpdate.once { - if (isActive && opModeControlsPanel.currentOpMode != eocvSim.pipelineManager.currentPipeline && eocvSim.pipelineManager.currentPipeline != null) { - val opMode = eocvSim.pipelineManager.currentPipeline + pipelineManager.onUpdate.once { + if (isActive && opModeControlsPanel.currentOpMode != pipelineManager.currentPipeline && pipelineManager.currentPipeline != null) { + val opMode = pipelineManager.currentPipeline if (opMode is OpMode) { val name = if (opMode.opModeType == OpModeType.AUTONOMOUS) @@ -237,7 +245,7 @@ class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeC logger.info("External change detected \"$name\"") - opModeSelected(eocvSim.pipelineManager.currentPipelineIndex, name, false) + opModeSelected(pipelineManager.currentPipelineIndex, name, false) } else if (isActive) { reset(-1) } @@ -245,16 +253,17 @@ class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeC } } - eocvSim.pipelineManager.onPipelineListRefresh { + pipelineManager.onPipelineListRefresh { updateOpModesList() } - eocvSim.pipelineManager.onExternalSwitchingEnable { + pipelineManager.onExternalSwitchingEnable { allowOpModeSwitching = true } - eocvSim.pipelineManager.onExternalSwitchingDisable { + pipelineManager.onExternalSwitchingDisable { allowOpModeSwitching = false } + } private fun teleOpSelected(index: Int) { @@ -298,7 +307,8 @@ class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeC val autonomousListModel = DefaultListModel() val teleopListModel = DefaultListModel() - pipelinesData = eocvSim.pipelineManager.pipelines.toArray(arrayOf()) + pipelinesData = pipelineManager.pipelines.toArray(arrayOf()) + var autonomousSelectorIndex = Range.clip(autonomousListModel.size() - 1, 0, Int.MAX_VALUE) var teleopSelectorIndex = Range.clip(teleopListModel.size() - 1, 0, Int.MAX_VALUE) @@ -306,7 +316,8 @@ class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeC autonomousIndexMap.clear() teleopIndexMap.clear() - for ((managerIndex, pipeline) in eocvSim.pipelineManager.pipelines.withIndex()) { + for ((managerIndex, pipeline) in pipelineManager.pipelines.withIndex()) { + if (ReflectUtil.hasSuperclass(pipeline.clazz, OpMode::class.java)) { val type = pipeline.clazz.opModeType @@ -345,28 +356,27 @@ class OpModeSelectorPanel(val eocvSim: EOCVSim, val opModeControlsPanel: OpModeC val opMode = opModeControlsPanel.currentOpMode - if (eocvSim.pipelineManager.currentPipeline == opMode && opMode != null && opMode.notifier.state != OpModeState.SELECTED) { - opMode.notifier?.onStateChange?.let { it -> - it { - val state = opMode.notifier.state + if (pipelineManager.currentPipeline == opMode && opMode != null && opMode.notifier.state != OpModeState.SELECTED) { + opMode.notifier?.onStateChange?.attach { + val state = opMode.notifier.state - if (state == OpModeState.STOPPED) { - removeListener() + if (state == OpModeState.STOPPED) { + removeListener() - if (nextPipeline == null || nextPipeline >= 0) { - eocvSim.pipelineManager.onUpdate.once { - eocvSim.pipelineManager.changePipeline(nextPipeline) - } + if (nextPipeline == null || nextPipeline >= 0) { + pipelineManager.onUpdate.once { + pipelineManager.changePipeline(nextPipeline) } } } } } else if (nextPipeline == null || nextPipeline >= 0) { - eocvSim.pipelineManager.onUpdate.once { - eocvSim.pipelineManager.requestChangePipeline(nextPipeline) + pipelineManager.onUpdate.once { + pipelineManager.requestChangePipeline(nextPipeline) } } + _selectedIndex = -1 opModeControlsPanel.stopCurrentOpMode() } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/SidebarOpModeTabPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/SidebarOpModeTabPanel.kt index 7a210f5e..521870e9 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/SidebarOpModeTabPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/SidebarOpModeTabPanel.kt @@ -23,7 +23,6 @@ package com.github.serivesmejia.eocvsim.gui.component.visualizer.opmode -import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.gui.component.visualizer.SidebarPanel import com.github.serivesmejia.eocvsim.gui.component.visualizer.TelemetryPanel import java.awt.Font @@ -32,12 +31,17 @@ import javax.swing.BoxLayout import javax.swing.JPanel import javax.swing.border.EmptyBorder -class SidebarOpModeTabPanel(eocvSim: EOCVSim) : SidebarPanel.TabJPanel() { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject - val opModeControlsPanel = OpModeControlsPanel(eocvSim) - val opModeSelectorPanel = OpModeSelectorPanel(eocvSim, opModeControlsPanel) +class SidebarOpModeTabPanel : SidebarPanel.TabJPanel(), KoinComponent { + + val opModeControlsPanel = OpModeControlsPanel() + val opModeSelectorPanel = OpModeSelectorPanel(opModeControlsPanel) + + + val telemetryPanel = TelemetryPanel() - val telemetryPanel = TelemetryPanel(eocvSim.pipelineManager) init { font = font.deriveFont(Font.PLAIN, 14f) 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 d0ca7c1d..948f8696 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 @@ -23,8 +23,12 @@ package com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline -import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import com.github.serivesmejia.eocvsim.output.RecordingManager +import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.gui.DialogFactory +import org.koin.core.qualifier.named + import com.github.serivesmejia.eocvsim.gui.component.PopupX import java.awt.GridBagConstraints import java.awt.GridBagLayout @@ -35,7 +39,17 @@ import javax.swing.JPanel import javax.swing.JToggleButton import javax.swing.SwingUtilities -class PipelineSelectorButtonsPanel(eocvSim: EOCVSim) : JPanel(GridBagLayout()) { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class PipelineSelectorButtonsPanel : JPanel(GridBagLayout()), KoinComponent { + + private val pipelineManager: PipelineManager by inject() + private val recordingManager: RecordingManager by inject() + private val onMainUpdate: EventHandler by inject(named("onMainLoop")) + + private val dialogFactory: DialogFactory by inject() + val pipelinePauseBtt = JToggleButton("Pause") val pipelineRecordBtt = JToggleButton("Record") @@ -50,8 +64,9 @@ class PipelineSelectorButtonsPanel(eocvSim: EOCVSim) : JPanel(GridBagLayout()) { init { //listener for changing pause state pipelinePauseBtt.addActionListener { - eocvSim.onMainUpdate.once { eocvSim.pipelineManager.setPaused(pipelinePauseBtt.isSelected) } + onMainUpdate.once { pipelineManager.setPaused(pipelinePauseBtt.isSelected) } } + pipelinePauseBtt.addChangeListener { pipelinePauseBtt.text = if(pipelinePauseBtt.isSelected) "Resume" else "Pause" } @@ -61,14 +76,15 @@ class PipelineSelectorButtonsPanel(eocvSim: EOCVSim) : JPanel(GridBagLayout()) { }) pipelineRecordBtt.addActionListener { - eocvSim.onMainUpdate.once { + onMainUpdate.once { if (pipelineRecordBtt.isSelected) { - if (!eocvSim.isCurrentlyRecording()) eocvSim.startRecordingSession() + if (!recordingManager.isCurrentlyRecording()) recordingManager.startRecordingSession() } else { - if (eocvSim.isCurrentlyRecording()) eocvSim.stopRecordingSession() + if (recordingManager.isCurrentlyRecording()) recordingManager.stopRecordingSession() } } } + add(pipelineRecordBtt, GridBagConstraints().apply { gridx = 1 }) pipelineWorkspaceBtt.addActionListener { @@ -103,7 +119,7 @@ class PipelineSelectorButtonsPanel(eocvSim: EOCVSim) : JPanel(GridBagLayout()) { val selectWorkspBtt = JButton("Select Workspace") - selectWorkspBtt.addActionListener { DialogFactory.createWorkspace(eocvSim.visualizer) } + selectWorkspBtt.addActionListener { dialogFactory.createWorkspace() } workspaceButtonsPanel.add(selectWorkspBtt, GridBagConstraints().apply { gridx = 0 gridy = 0 @@ -114,7 +130,8 @@ class PipelineSelectorButtonsPanel(eocvSim: EOCVSim) : JPanel(GridBagLayout()) { gridy = 0 }) - pipelineCompileBtt.addActionListener { eocvSim.pipelineManager.compiledPipelineManager.asyncBuild() } + pipelineCompileBtt.addActionListener { pipelineManager.compiledPipelineManager.asyncBuild() } + workspaceButtonsPanel.add(pipelineCompileBtt, GridBagConstraints().apply { gridx = 2 gridy = 0 @@ -122,7 +139,7 @@ class PipelineSelectorButtonsPanel(eocvSim: EOCVSim) : JPanel(GridBagLayout()) { val outputBtt = JButton("Pipeline Output") - outputBtt.addActionListener { DialogFactory.createPipelineOutput(eocvSim) } + outputBtt.addActionListener { dialogFactory.createPipelineOutput() } workspaceButtonsPanel.add(outputBtt, GridBagConstraints().apply { gridy = 1 diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/PipelineSelectorPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/PipelineSelectorPanel.kt index 3a337755..5f60c252 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/PipelineSelectorPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/PipelineSelectorPanel.kt @@ -23,8 +23,9 @@ package com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline -import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.gui.util.icon.PipelineListIconRenderer + import com.github.serivesmejia.eocvsim.pipeline.PipelineData import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.util.ReflectUtil @@ -41,7 +42,15 @@ import java.awt.event.MouseAdapter import java.awt.event.MouseEvent import javax.swing.* -class PipelineSelectorPanel(private val eocvSim: EOCVSim) : JPanel() { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class PipelineSelectorPanel : JPanel(), KoinComponent { + + private val pipelineManager: PipelineManager by inject() + private val visualizer: Visualizer by inject() + + var selectedIndex: Int get() = indexMap[pipelineSelector.selectedIndex] ?: -1 @@ -61,7 +70,8 @@ class PipelineSelectorPanel(private val eocvSim: EOCVSim) : JPanel() { // private val indexMap = mutableMapOf() - val buttonsPanel = PipelineSelectorButtonsPanel(eocvSim) + val buttonsPanel = PipelineSelectorButtonsPanel() + var allowPipelineSwitching = false @@ -77,7 +87,8 @@ class PipelineSelectorPanel(private val eocvSim: EOCVSim) : JPanel() { pipelineSelectorLabel.horizontalAlignment = JLabel.CENTER - pipelineSelector.cellRenderer = PipelineListIconRenderer(eocvSim.pipelineManager) { indexMap } + pipelineSelector.cellRenderer = PipelineListIconRenderer(pipelineManager) { indexMap } + pipelineSelector.selectionMode = ListSelectionModel.SINGLE_SELECTION pipelineSelectorScroll.setViewportView(pipelineSelector) @@ -114,47 +125,49 @@ class PipelineSelectorPanel(private val eocvSim: EOCVSim) : JPanel() { val pipeline = indexMap[index] ?: return if (pipeline != beforeSelectedPipeline) { - if (!eocvSim.pipelineManager.paused) { - eocvSim.pipelineManager.requestChangePipeline(pipeline) + if (!pipelineManager.paused) { + pipelineManager.requestChangePipeline(pipeline) beforeSelectedPipeline = pipeline } else { - if (eocvSim.pipelineManager.pauseReason !== PipelineManager.PauseReason.IMAGE_ONE_ANALYSIS) { + if (pipelineManager.pauseReason !== PipelineManager.PauseReason.IMAGE_ONE_ANALYSIS) { pipelineSelector.setSelectedIndex(beforeSelectedPipeline) } else { //handling pausing - eocvSim.pipelineManager.requestSetPaused(false) - eocvSim.pipelineManager.requestChangePipeline(pipeline) + pipelineManager.requestSetPaused(false) + pipelineManager.requestChangePipeline(pipeline) beforeSelectedPipeline = pipeline } } } + } else { pipelineSelector.setSelectedIndex(0) } } }) - eocvSim.pipelineManager.onPipelineChange { - selectedIndex = eocvSim.pipelineManager.currentPipelineIndex + pipelineManager.onPipelineChange { + selectedIndex = pipelineManager.currentPipelineIndex } - eocvSim.pipelineManager.onPipelineListRefresh { + pipelineManager.onPipelineListRefresh { updatePipelinesList() } - eocvSim.pipelineManager.onExternalSwitchingEnable { + pipelineManager.onExternalSwitchingEnable { allowPipelineSwitching = true } - eocvSim.pipelineManager.onExternalSwitchingDisable { + pipelineManager.onExternalSwitchingDisable { allowPipelineSwitching = false } val pauseListener: EventListener = { - eocvSim.visualizer.pipelineSelectorPanel.buttonsPanel.pipelinePauseBtt.isSelected = - eocvSim.pipelineManager.paused + visualizer.pipelineSelectorPanel.buttonsPanel.pipelinePauseBtt.isSelected = + pipelineManager.paused } - eocvSim.pipelineManager.onPause(pauseListener) - eocvSim.pipelineManager.onResume(pauseListener) + pipelineManager.onPause(pauseListener) + pipelineManager.onResume(pauseListener) + } fun updatePipelinesList() = SwingUtilities.invokeLater { @@ -163,9 +176,10 @@ class PipelineSelectorPanel(private val eocvSim: EOCVSim) : JPanel() { indexMap.clear() - pipelinesData = eocvSim.pipelineManager.pipelines.toArray(arrayOf()) + pipelinesData = pipelineManager.pipelines.toArray(arrayOf()) + + for ((managerIndex, pipeline) in pipelineManager.pipelines.withIndex()) { - for ((managerIndex, pipeline) in eocvSim.pipelineManager.pipelines.withIndex()) { if (!ReflectUtil.hasSuperclass(pipeline.clazz, OpMode::class.java) && !pipeline.hidden) { listModel.addElement(pipeline.clazz.simpleName) indexMap[selectorIndex] = managerIndex diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SidebarPipelineTabPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SidebarPipelineTabPanel.kt index ffb5e988..4881da33 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SidebarPipelineTabPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SidebarPipelineTabPanel.kt @@ -23,7 +23,8 @@ package com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline -import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager + import com.github.serivesmejia.eocvsim.gui.component.visualizer.SidebarPanel import com.github.serivesmejia.eocvsim.gui.component.visualizer.TelemetryPanel import java.awt.Font @@ -34,12 +35,20 @@ import javax.swing.JPanel import javax.swing.border.EmptyBorder import javax.swing.border.TitledBorder -class SidebarPipelineTabPanel(private val eocvSim: EOCVSim) : SidebarPanel.TabJPanel() { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class SidebarPipelineTabPanel : SidebarPanel.TabJPanel(), KoinComponent { + + private val pipelineManager: PipelineManager by inject() + - val pipelineSelectorPanel = PipelineSelectorPanel(eocvSim) - val sourceSelectorPanel = SourceSelectorPanel(eocvSim) + val pipelineSelectorPanel = PipelineSelectorPanel() + val sourceSelectorPanel = SourceSelectorPanel() + + + val telemetryPanel = TelemetryPanel() - val telemetryPanel = TelemetryPanel(eocvSim.pipelineManager) init { font = font.deriveFont(Font.PLAIN, 14f) @@ -93,11 +102,12 @@ class SidebarPipelineTabPanel(private val eocvSim: EOCVSim) : SidebarPanel.TabJP override fun onActivated() { pipelineSelectorPanel.isActive = true - eocvSim.pipelineManager.onUpdate.once { - eocvSim.pipelineManager.changePipeline(0) + pipelineManager.onUpdate.once { + pipelineManager.changePipeline(0) } } + override fun onDeactivated() { pipelineSelectorPanel.isActive = false } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt index 99beb676..e0479084 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt @@ -24,7 +24,10 @@ package com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.input.InputSourceManager +import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.gui.DialogFactory +import org.koin.core.qualifier.named import com.github.serivesmejia.eocvsim.gui.util.icon.SourcesListIconRenderer import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.util.extension.clipUpperZero @@ -39,7 +42,15 @@ import java.awt.GridBagLayout import java.awt.event.MouseAdapter import javax.swing.* -class SourceSelectorPanel(private val eocvSim: EOCVSim) : JPanel() { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class SourceSelectorPanel : JPanel(), KoinComponent { + + private val inputSourceManager: InputSourceManager by inject() + private val pipelineManager: PipelineManager by inject() + private val onMainUpdate: EventHandler by inject(named("onMainLoop")) + private val dialogFactory: DialogFactory by inject() val sourceSelector = JList() val sourceSelectorScroll = JScrollPane() @@ -83,10 +94,11 @@ class SourceSelectorPanel(private val eocvSim: EOCVSim) : JPanel() { }) //different icons - sourceSelector.cellRenderer = SourcesListIconRenderer(eocvSim.inputSourceManager) + sourceSelector.cellRenderer = SourcesListIconRenderer(inputSourceManager) + sourceSelectorCreateBtt.addActionListener { - DialogFactory.createSourceExDialog(eocvSim) + dialogFactory.createSourceExDialog() } sourceSelectorButtonsContainer = JPanel(FlowLayout(FlowLayout.CENTER)) @@ -118,24 +130,26 @@ class SourceSelectorPanel(private val eocvSim: EOCVSim) : JPanel() { //enable or disable source delete button depending if source is default or not sourceSelectorDeleteBtt.isEnabled = - eocvSim.inputSourceManager.sources[source]?.isDefault == false + inputSourceManager.sources[source]?.isDefault == false if (source != beforeSelectedSource) { - if (!eocvSim.pipelineManager.paused) { - eocvSim.inputSourceManager.requestSetInputSource(source) + if (!pipelineManager.paused) { + inputSourceManager.requestSetInputSource(source) beforeSelectedSource = source beforeSelectedSourceIndex = sourceSelector.selectedIndex + } else { //check if the user requested the pause or if it was due to one shoot analysis when selecting images - if (eocvSim.pipelineManager.pauseReason !== PipelineManager.PauseReason.IMAGE_ONE_ANALYSIS) { + if (pipelineManager.pauseReason !== PipelineManager.PauseReason.IMAGE_ONE_ANALYSIS) { sourceSelector.setSelectedIndex(beforeSelectedSourceIndex) } else { //handling pausing - eocvSim.pipelineManager.requestSetPaused(false) - eocvSim.inputSourceManager.requestSetInputSource(source) + pipelineManager.requestSetPaused(false) + inputSourceManager.requestSetInputSource(source) beforeSelectedSource = source beforeSelectedSourceIndex = sourceSelector.selectedIndex } } + } } else { sourceSelector.setSelectedIndex(1) @@ -152,51 +166,54 @@ class SourceSelectorPanel(private val eocvSim: EOCVSim) : JPanel() { val index = sourceSelector.selectedIndex val source = sourceSelector.model.getElementAt(index) - eocvSim.onMainUpdate.once { - eocvSim.inputSourceManager.deleteInputSource(source) + onMainUpdate.once { + inputSourceManager.deleteInputSource(source) updateSourcesList() sourceSelector.selectedIndex = (index - 1).clipUpperZero() } + } - eocvSim.inputSourceManager.onInputSourceRemoved { + inputSourceManager.onInputSourceRemoved { updateSourcesList() } - eocvSim.inputSourceManager.onInputSourceAdded { - val name = eocvSim.inputSourceManager.lastAddedSourceName - val dispatchedByUser = eocvSim.inputSourceManager.wasLastSourceAddedByUser + inputSourceManager.onInputSourceAdded { + val name = inputSourceManager.lastAddedSourceName + val dispatchedByUser = inputSourceManager.wasLastSourceAddedByUser updateSourcesList() - + SwingUtilities.invokeLater { val currentIndex = sourceSelector.selectedIndex - + if (dispatchedByUser) { val index = getIndexOf(name) sourceSelector.selectedIndex = index - eocvSim.inputSourceManager.requestSetInputSource(name) + inputSourceManager.requestSetInputSource(name) - eocvSim.onMainUpdate.once { - eocvSim.pipelineManager.requestSetPaused(false) - eocvSim.inputSourceManager.pauseIfImageTwoFrames() + onMainUpdate.once { + pipelineManager.requestSetPaused(false) + inputSourceManager.pauseIfImageTwoFrames() } } else { sourceSelector.selectedIndex = currentIndex } - + allowSourceSwitching = true } } + } fun updateSourcesList(): Job { SwingUtilities.invokeLater { val listModel = DefaultListModel() - eocvSim.inputSourceManager.sortedInputSources.forEach { source -> + inputSourceManager.sortedInputSources.forEach { source -> + listModel.addElement(source.name) } 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 deleted file mode 100644 index f215630b..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -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; -import com.github.serivesmejia.eocvsim.util.StrUtil; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import java.awt.*; -import java.io.UnsupportedEncodingException; -import java.net.URI; -import java.nio.charset.StandardCharsets; - -public class About { - - public JDialog about = null; - - public static ListModel CONTRIBS_LIST_MODEL; - public static ListModel OSL_LIST_MODEL; - - static { - try { - CONTRIBS_LIST_MODEL = GuiUtil.isToListModel(About.class.getResourceAsStream("/contributors.txt"), StandardCharsets.UTF_8); - OSL_LIST_MODEL = GuiUtil.isToListModel(About.class.getResourceAsStream("/opensourcelibs.txt"), StandardCharsets.UTF_8); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - } - - public About(JFrame parent, EOCVSim eocvSim) { - about = new JDialog(parent); - initAbout(); - } - - private void initAbout() { - - about.setModal(true); - - about.setTitle("About"); - - JPanel contents = new JPanel(new GridLayout(2, 1)); - contents.setAlignmentX(Component.CENTER_ALIGNMENT); - - ImageX icon = new ImageX(EOCVSimIconLibrary.INSTANCE.getIcoEOCVSim64()); - icon.setSize(50, 50); - icon.setAlignmentX(Component.CENTER_ALIGNMENT); - - icon.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); - - JLabel appInfo = new JLabel("EasyOpenCV Simulator v" + EOCVSim.VERSION); - appInfo.setFont(appInfo.getFont().deriveFont(appInfo.getFont().getStyle() | Font.BOLD)); //set font to bold - - JPanel appInfoLogo = new JPanel(new FlowLayout()); - - appInfoLogo.add(icon); - appInfoLogo.add(appInfo); - - appInfoLogo.setBorder(BorderFactory.createEmptyBorder(10, 10, -30, 10)); - - contents.add(appInfoLogo); - - JTabbedPane tabbedPane = new JTabbedPane(); - - JPanel contributors = new JPanel(new FlowLayout(FlowLayout.CENTER)); - - JList contribsList = new JList<>(); - contribsList.setModel(CONTRIBS_LIST_MODEL); - contribsList.setSelectionModel(new GuiUtil.NoSelectionModel()); - contribsList.setLayout(new FlowLayout(FlowLayout.CENTER)); - contribsList.setAlignmentY(Component.TOP_ALIGNMENT); - - contribsList.setVisibleRowCount(4); - - JPanel contributorsList = new JPanel(new FlowLayout(FlowLayout.CENTER)); - contributorsList.setAlignmentY(Component.TOP_ALIGNMENT); - - JScrollPane contribsListScroll = new JScrollPane(); - contribsListScroll.setBorder(new EmptyBorder(0,0,20,10)); - contribsListScroll.setAlignmentX(Component.CENTER_ALIGNMENT); - contribsListScroll.setAlignmentY(Component.TOP_ALIGNMENT); - contribsListScroll.setViewportView(contribsList); - - contributors.setAlignmentY(Component.TOP_ALIGNMENT); - contents.setAlignmentY(Component.TOP_ALIGNMENT); - - contributorsList.add(contribsListScroll); - contributors.add(contributorsList); - - tabbedPane.addTab("Contributors", contributors); - - JPanel osLibs = new JPanel(new FlowLayout(FlowLayout.CENTER)); - - JList osLibsList = new JList<>(); - osLibsList.setModel(OSL_LIST_MODEL); - osLibsList.setLayout(new FlowLayout(FlowLayout.CENTER)); - osLibsList.setAlignmentY(Component.TOP_ALIGNMENT); - - osLibsList.setVisibleRowCount(4); - - osLibsList.addListSelectionListener(e -> { - if(!e.getValueIsAdjusting()) { - - String text = osLibsList.getSelectedValue(); - String[] urls = StrUtil.findUrlsInString(text); - - if(urls.length > 0) { - try { - Desktop.getDesktop().browse(new URI(urls[0])); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - - osLibsList.clearSelection(); - - } - }); - - JPanel osLibsListPane = new JPanel(new FlowLayout(FlowLayout.CENTER)); - osLibsList.setAlignmentY(Component.TOP_ALIGNMENT); - - JScrollPane osLibsListScroll = new JScrollPane(); - osLibsListScroll.setBorder(new EmptyBorder(0,0,20,10)); - osLibsListScroll.setAlignmentX(Component.CENTER_ALIGNMENT); - osLibsListScroll.setAlignmentY(Component.TOP_ALIGNMENT); - osLibsListScroll.setViewportView(osLibsList); - - osLibs.setAlignmentY(Component.TOP_ALIGNMENT); - - osLibsListPane.add(osLibsListScroll); - osLibs.add(osLibsListPane); - - tabbedPane.addTab("Open Source Libraries", osLibs); - - contents.add(tabbedPane); - - contents.setBorder(new EmptyBorder(10,10,10,10)); - - about.add(contents); - - about.pack(); - about.setLocationRelativeTo(null); - about.setResizable(false); - about.setVisible(true); - } - -} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.kt new file mode 100644 index 00000000..5df638da --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.kt @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2021 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +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.Visualizer +import com.github.serivesmejia.eocvsim.gui.util.GuiUtil +import com.github.serivesmejia.eocvsim.util.StrUtil +import io.github.deltacv.vision.external.gui.component.ImageX +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import java.awt.* +import java.net.URI +import java.nio.charset.StandardCharsets +import javax.swing.* +import javax.swing.border.EmptyBorder + +class About : KoinComponent { + + private val visualizer: Visualizer by inject() + + val about = JDialog(visualizer.frame) + + companion object { + var CONTRIBS_LIST_MODEL: ListModel? = null + var OSL_LIST_MODEL: ListModel? = null + + init { + try { + CONTRIBS_LIST_MODEL = GuiUtil.isToListModel(About::class.java.getResourceAsStream("/contributors.txt"), StandardCharsets.UTF_8) + OSL_LIST_MODEL = GuiUtil.isToListModel(About::class.java.getResourceAsStream("/opensourcelibs.txt"), StandardCharsets.UTF_8) + } catch (e: Exception) { + e.printStackTrace() + } + } + } + + init { + initAbout() + } + + private fun initAbout() { + about.isModal = true + about.title = "About" + + val contents = JPanel(GridLayout(2, 1)) + contents.alignmentX = Component.CENTER_ALIGNMENT + + val icon = ImageX(EOCVSimIconLibrary.icoEOCVSim64) + icon.setSize(50, 50) + icon.alignmentX = Component.CENTER_ALIGNMENT + icon.border = BorderFactory.createEmptyBorder(10, 10, 10, 10) + + val appInfo = JLabel("EasyOpenCV Simulator v" + EOCVSim.VERSION) + appInfo.font = appInfo.font.deriveFont(appInfo.font.style or Font.BOLD) // set font to bold + + val appInfoLogo = JPanel(FlowLayout()) + appInfoLogo.add(icon) + appInfoLogo.add(appInfo) + appInfoLogo.border = BorderFactory.createEmptyBorder(10, 10, -30, 10) + + contents.add(appInfoLogo) + + val tabbedPane = JTabbedPane() + + val contributors = JPanel(FlowLayout(FlowLayout.CENTER)) + val contribsList = JList() + contribsList.model = CONTRIBS_LIST_MODEL + contribsList.selectionModel = GuiUtil.NoSelectionModel() + contribsList.layout = FlowLayout(FlowLayout.CENTER) + contribsList.alignmentY = Component.TOP_ALIGNMENT + contribsList.visibleRowCount = 4 + + val contributorsList = JPanel(FlowLayout(FlowLayout.CENTER)) + contributorsList.alignmentY = Component.TOP_ALIGNMENT + + val contribsListScroll = JScrollPane() + contribsListScroll.border = EmptyBorder(0, 0, 20, 10) + contribsListScroll.alignmentX = Component.CENTER_ALIGNMENT + contribsListScroll.alignmentY = Component.TOP_ALIGNMENT + contribsListScroll.setViewportView(contribsList) + + contributors.alignmentY = Component.TOP_ALIGNMENT + contents.alignmentY = Component.TOP_ALIGNMENT + + contributorsList.add(contribsListScroll) + contributors.add(contributorsList) + + tabbedPane.addTab("Contributors", contributors) + + val osLibs = JPanel(FlowLayout(FlowLayout.CENTER)) + val osLibsList = JList() + osLibsList.model = OSL_LIST_MODEL + osLibsList.layout = FlowLayout(FlowLayout.CENTER) + osLibsList.alignmentY = Component.TOP_ALIGNMENT + osLibsList.visibleRowCount = 4 + + osLibsList.addListSelectionListener { e -> + if (!e.valueIsAdjusting) { + val text = osLibsList.selectedValue + if (text != null) { + val urls = StrUtil.findUrlsInString(text) + if (urls.isNotEmpty()) { + try { + Desktop.getDesktop().browse(URI(urls[0])) + } catch (ex: Exception) { + ex.printStackTrace() + } + } + } + osLibsList.clearSelection() + } + } + + val osLibsListPane = JPanel(FlowLayout(FlowLayout.CENTER)) + osLibsList.alignmentY = Component.TOP_ALIGNMENT + + val osLibsListScroll = JScrollPane() + osLibsListScroll.border = EmptyBorder(0, 0, 20, 10) + osLibsListScroll.alignmentX = Component.CENTER_ALIGNMENT + osLibsListScroll.alignmentY = Component.TOP_ALIGNMENT + osLibsListScroll.setViewportView(osLibsList) + + osLibs.alignmentY = Component.TOP_ALIGNMENT + osLibsListPane.add(osLibsListScroll) + osLibs.add(osLibsListPane) + + tabbedPane.addTab("Open Source Libraries", osLibs) + + contents.add(tabbedPane) + contents.border = EmptyBorder(10, 10, 10, 10) + + about.add(contents) + about.pack() + about.setLocationRelativeTo(null) + about.isResizable = false + about.isVisible = true + } +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.kt index a7470c0b..730f67d8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.kt @@ -25,24 +25,36 @@ package com.github.serivesmejia.eocvsim.gui.dialog import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.config.Config +import com.github.serivesmejia.eocvsim.config.ConfigManager +import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.gui.component.input.EnumComboBox import com.github.serivesmejia.eocvsim.gui.component.input.SizeFields import com.github.serivesmejia.eocvsim.gui.theme.Theme import com.github.serivesmejia.eocvsim.gui.util.WebcamDriver import com.github.serivesmejia.eocvsim.pipeline.PipelineFps import com.github.serivesmejia.eocvsim.pipeline.PipelineTimeout +import com.github.serivesmejia.eocvsim.util.event.EventHandler import java.awt.FlowLayout import java.awt.GridBagConstraints import java.awt.GridBagLayout import java.awt.GridLayout import javax.swing.* -class Configuration(parent: JFrame, private val eocvSim: EOCVSim) { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named + +class Configuration : KoinComponent { + + private val visualizer: Visualizer by inject() + private val configManager: ConfigManager by inject() + private val onMainLoop: EventHandler by inject(named("onMainLoop")) + + private val config get() = configManager.config + + private val dialog = JDialog(visualizer.frame) - private val dialog = JDialog(parent) - private val config: Config = eocvSim.configManager.config - // Declaring the components as 'val' private val themeComboBox: JComboBox private val superAccessCheckBox: JCheckBox private val prefersPaperVisionCheckbox: JCheckBox @@ -55,8 +67,6 @@ class Configuration(parent: JFrame, private val eocvSim: EOCVSim) { private val acceptButton: JButton init { - eocvSim.visualizer.childDialogs.add(dialog) - // --- Interface Tab --- themeComboBox = JComboBox().apply { Theme.entries.forEach { addItem(it.name.replace("_", " ")) } @@ -139,7 +149,7 @@ class Configuration(parent: JFrame, private val eocvSim: EOCVSim) { } acceptButton.addActionListener { - eocvSim.onMainUpdate.once { + onMainLoop.once { applyChanges() } @@ -183,10 +193,11 @@ class Configuration(parent: JFrame, private val eocvSim: EOCVSim) { config.autoAcceptSuperAccessOnTrusted = superAccessCheckBox.isSelected config.flags["prefersPaperVision"] = prefersPaperVisionCheckbox.isSelected - eocvSim.configManager.saveToFile() + configManager.saveToFile() if (userSelectedTheme != previousTheme) { - eocvSim.restart() + // TODO: Implement restart() + // eocvSim.restart() } } 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 index d519d3f6..5b8449d7 100644 --- 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 @@ -13,12 +13,15 @@ import javax.swing.JLabel import javax.swing.JOptionPane import javax.swing.JPanel -class CreateWorkspace( - val parent: JFrame, - val visualizer: Visualizer -) { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class CreateWorkspace : KoinComponent { + + val visualizer: Visualizer by inject() + + val dialog = JDialog(visualizer.frame) - val dialog = JDialog(parent) init { dialog.isModal = true @@ -57,7 +60,8 @@ class CreateWorkspace( dialog.dispose() JOptionPane.showConfirmDialog( - this@CreateWorkspace.parent, + visualizer.frame, + "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, diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/FileAlreadyExists.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/FileAlreadyExists.java deleted file mode 100644 index 6c46d02a..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/FileAlreadyExists.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.gui.dialog; - -import com.github.serivesmejia.eocvsim.EOCVSim; - -import javax.swing.*; -import java.awt.*; - -public class FileAlreadyExists { - - public static volatile boolean alreadyOpened = false; - public volatile JDialog fileAlreadyExists = null; - public volatile JPanel contentsPanel = new JPanel(); - public volatile UserChoice userChoice; - EOCVSim eocvSim = null; - - public FileAlreadyExists(JFrame parent, EOCVSim eocvSim) { - - fileAlreadyExists = new JDialog(parent); - - this.eocvSim = eocvSim; - } - - public UserChoice run() { - fileAlreadyExists.setModal(true); - - fileAlreadyExists.setTitle("Warning"); - - JPanel alreadyExistsPanel = new JPanel(new FlowLayout()); - - JLabel alreadyExistsLabel = new JLabel("File already exists in the selected directory"); - alreadyExistsPanel.add(alreadyExistsLabel); - - contentsPanel.add(alreadyExistsPanel); - - JPanel replaceCancelPanel = new JPanel(new FlowLayout()); - - JButton replaceButton = new JButton("Replace"); - replaceCancelPanel.add(replaceButton); - - replaceButton.addActionListener((e) -> { - userChoice = UserChoice.REPLACE; - fileAlreadyExists.setVisible(false); - }); - - JButton cancelButton = new JButton("Cancel"); - replaceCancelPanel.add(cancelButton); - - cancelButton.addActionListener((e) -> { - userChoice = UserChoice.CANCEL; - fileAlreadyExists.setVisible(false); - }); - - contentsPanel.add(replaceCancelPanel); - - fileAlreadyExists.add(contentsPanel); - - fileAlreadyExists.setResizable(false); - fileAlreadyExists.setLocationRelativeTo(null); - fileAlreadyExists.setVisible(true); - - while (userChoice == UserChoice.NA); - - return userChoice; - } - - public enum UserChoice {NA, REPLACE, CANCEL} - -} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/FileAlreadyExists.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/FileAlreadyExists.kt new file mode 100644 index 00000000..c0eadb55 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/FileAlreadyExists.kt @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.gui.dialog + +import com.github.serivesmejia.eocvsim.gui.Visualizer +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import java.awt.FlowLayout +import javax.swing.* + +class FileAlreadyExists : KoinComponent { + + private val visualizer: Visualizer by inject() + + var fileAlreadyExists = JDialog(visualizer.frame) + var contentsPanel = JPanel() + var userChoice: UserChoice = UserChoice.NA + + fun run(): UserChoice { + fileAlreadyExists.isModal = true + fileAlreadyExists.title = "Warning" + + contentsPanel.layout = BoxLayout(contentsPanel, BoxLayout.Y_AXIS) + + val alreadyExistsPanel = JPanel(FlowLayout()) + val alreadyExistsLabel = JLabel("File already exists in the selected directory") + alreadyExistsPanel.add(alreadyExistsLabel) + + contentsPanel.add(alreadyExistsPanel) + + val replaceCancelPanel = JPanel(FlowLayout()) + + val replaceButton = JButton("Replace") + replaceCancelPanel.add(replaceButton) + + replaceButton.addActionListener { + userChoice = UserChoice.REPLACE + fileAlreadyExists.isVisible = false + } + + val cancelButton = JButton("Cancel") + replaceCancelPanel.add(cancelButton) + + cancelButton.addActionListener { + userChoice = UserChoice.CANCEL + fileAlreadyExists.isVisible = false + } + + contentsPanel.add(replaceCancelPanel) + + fileAlreadyExists.add(contentsPanel) + fileAlreadyExists.pack() + fileAlreadyExists.isResizable = false + fileAlreadyExists.setLocationRelativeTo(null) + fileAlreadyExists.isVisible = true + + return userChoice + } + + enum class UserChoice { NA, REPLACE, CANCEL } +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Output.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Output.kt index b6ebb307..7fbbfff8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Output.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Output.kt @@ -24,8 +24,12 @@ package com.github.serivesmejia.eocvsim.gui.dialog import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.gui.dialog.component.OutputPanel +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import com.github.serivesmejia.eocvsim.pipeline.compiler.CompiledPipelineManager import com.github.serivesmejia.eocvsim.pipeline.compiler.PipelineCompileStatus +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope @@ -36,12 +40,15 @@ import java.awt.event.WindowAdapter import java.awt.event.WindowEvent import javax.swing.* +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + class Output @JvmOverloads constructor( - parent: JFrame, - private val eocvSim: EOCVSim, private val tabbedPaneIndex: Int = latestIndex, private val wasManuallyOpened: Boolean = false -) { +) : KoinComponent { + + private val visualizer: Visualizer by inject() companion object { var isAlreadyOpened = false @@ -51,7 +58,7 @@ class Output @JvmOverloads constructor( private set } - private val output = JDialog(parent) + private val output = JDialog(visualizer.frame) private val buildBottomButtonsPanel = BuildOutputBottomButtonsPanel(::close) private val buildOutputPanel = OutputPanel(buildBottomButtonsPanel) @@ -61,14 +68,13 @@ class Output @JvmOverloads constructor( private val tabbedPane = JTabbedPane() - private val compiledPipelineManager = eocvSim.pipelineManager.compiledPipelineManager - private val pipelineExceptionTracker = eocvSim.pipelineManager.pipelineExceptionTracker + private val pipelineManager: PipelineManager by inject() + private val compiledPipelineManager: CompiledPipelineManager by inject() + private val scope: CoroutineScope by inject() init { isAlreadyOpened = true - eocvSim.visualizer.childDialogs.add(output) - output.isModal = true output.title = "Output" @@ -81,7 +87,7 @@ class Output @JvmOverloads constructor( updatePipelineOutput() - if(eocvSim.pipelineManager.paused) { + if(pipelineManager.paused) { pipelinePaused() } else { pipelineResumed() @@ -102,14 +108,14 @@ class Output @JvmOverloads constructor( } @OptIn(DelicateCoroutinesApi::class) - private fun registerListeners() = eocvSim.scope.launch(Dispatchers.Swing) { + private fun registerListeners() = scope.launch(Dispatchers.Swing) { output.addWindowListener(object: WindowAdapter() { override fun windowClosing(e: WindowEvent) { close() } }) - pipelineExceptionTracker.onUpdate { + pipelineManager.pipelineExceptionTracker.onUpdate { if(!output.isVisible) { removeListener() } else { @@ -134,7 +140,7 @@ class Output @JvmOverloads constructor( } } - eocvSim.pipelineManager.onPause { + pipelineManager.onPause { if(!output.isVisible) { removeListener() } else { @@ -142,7 +148,7 @@ class Output @JvmOverloads constructor( } } - eocvSim.pipelineManager.onResume { + pipelineManager.onResume { if(!output.isVisible) { removeListener() } else { @@ -151,7 +157,7 @@ class Output @JvmOverloads constructor( } pipelineBottomButtonsPanel.pauseButton.addActionListener { - eocvSim.pipelineManager.setPaused(pipelineBottomButtonsPanel.pauseButton.isSelected) + pipelineManager.setPaused(pipelineBottomButtonsPanel.pauseButton.isSelected) if(pipelineBottomButtonsPanel.pauseButton.isSelected) { pipelinePaused() @@ -161,16 +167,16 @@ class Output @JvmOverloads constructor( } pipelineBottomButtonsPanel.clearButton.addActionListener { - eocvSim.pipelineManager.pipelineExceptionTracker.clear() + pipelineManager.pipelineExceptionTracker.clear() } buildBottomButtonsPanel.buildAgainButton.addActionListener { - eocvSim.pipelineManager.compiledPipelineManager.asyncBuild() + pipelineManager.compiledPipelineManager.asyncBuild() } } private fun updatePipelineOutput() { - pipelineOutputPanel.outputArea.text = pipelineExceptionTracker.message + pipelineOutputPanel.outputArea.text = pipelineManager.pipelineExceptionTracker.message } private fun buildRunning() { 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 eca2dec0..9b1f6dd6 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 @@ -23,7 +23,7 @@ package com.github.serivesmejia.eocvsim.gui.dialog -import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.gui.dialog.component.BottomButtonsPanel import com.github.serivesmejia.eocvsim.gui.dialog.component.OutputPanel import io.github.deltacv.common.util.loggerForThis @@ -31,10 +31,8 @@ import io.github.deltacv.eocvsim.plugin.loader.FilePluginLoaderImpl 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 kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import kotlinx.coroutines.* import kotlinx.coroutines.swing.Swing import java.awt.Dimension import java.awt.GridBagConstraints @@ -49,7 +47,9 @@ import javax.swing.event.ChangeListener class PluginOutput( appendDelegate: AppendDelegate, val pluginManager: PluginManager, - val eocvSim: EOCVSim? = null, + val onRestartRequested: EventHandler, + val configManager: ConfigManager, + val scope: CoroutineScope, val onContinue: Runnable ) : Appendable { @@ -111,7 +111,8 @@ class PluginOutput( } @OptIn(DelicateCoroutinesApi::class) - private fun registerListeners() = (eocvSim?.scope ?: GlobalScope).launch(Dispatchers.Swing) { + private fun registerListeners() = scope.launch(Dispatchers.Swing) { + output.addWindowListener(object : java.awt.event.WindowAdapter() { override fun windowClosing(e: java.awt.event.WindowEvent?) { if(mavenBottomButtonsPanel.continueButton.isEnabled) { @@ -140,19 +141,19 @@ class PluginOutput( } private fun checkShouldAskForRestart() { - if(shouldAskForRestart && eocvSim != null) { + if(shouldAskForRestart) { val dialogResult = JOptionPane.showConfirmDialog( output, "You need to restart the application to apply the changes. Do you want to restart now?", "Restart required", JOptionPane.YES_NO_OPTION ) - + if(dialogResult == JOptionPane.YES_OPTION) { output.isVisible = false - eocvSim.restart() + onRestartRequested.run() } - + shouldAskForRestart = false } } @@ -360,14 +361,15 @@ class PluginOutput( ) if(dialogResult == JOptionPane.YES_OPTION) { - eocvSim?.config?.flags?.set("startFresh", true) - + configManager.config.flags["startFresh"] = true + PluginRepositoryManager.REPOSITORY_FILE.delete() PluginRepositoryManager.CACHE_FILE.delete() - + shouldAskForRestart = true checkShouldAskForRestart() } + } val closeButton = JButton("Close") 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 index 19eb4da7..6488df96 100644 --- 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 @@ -23,8 +23,10 @@ package com.github.serivesmejia.eocvsim.gui.dialog.iama +import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary import com.github.serivesmejia.eocvsim.gui.Visualizer +import com.github.serivesmejia.eocvsim.config.ConfigManager import java.awt.Dimension import java.awt.GridBagConstraints import java.awt.GridBagLayout @@ -36,12 +38,18 @@ import javax.swing.JFrame import javax.swing.JLabel import javax.swing.JPanel -class IAmA( - val parent: JFrame, - val visualizer: Visualizer -) { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class IAmA : KoinComponent { + + private val visualizer: Visualizer by inject() + private val configManager: ConfigManager by inject() + + private val dialogFactory: DialogFactory by inject() + + val dialog = JDialog(visualizer.frame) - val dialog = JDialog(parent) init { dialog.isModal = true @@ -78,7 +86,7 @@ class IAmA( addActionListener { dialog.dispose() - IAmAFirstRobotics(this@IAmA.parent, visualizer) + IAmAFirstRobotics() } }) @@ -92,7 +100,7 @@ class IAmA( addActionListener { dialog.dispose() - IAmAGeneralPublic(this@IAmA.parent, visualizer) + IAmAGeneralPublic() } }) @@ -106,7 +114,7 @@ class IAmA( addActionListener { dialog.dispose() - IAmAPaperVision(this@IAmA.parent, visualizer, specificallyInterested = true) + IAmAPaperVision(specificallyInterested = true) } }) @@ -125,7 +133,7 @@ class IAmA( dialog.defaultCloseOperation = JDialog.DISPOSE_ON_CLOSE dialog.setLocationRelativeTo(null) - visualizer.eocvSim.config.flags["hasShownIamA"] = true + configManager.config.flags["hasShownIamA"] = true dialog.isVisible = true } 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 index fd5b4331..485557c4 100644 --- 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 @@ -37,12 +37,15 @@ import javax.swing.JFrame import javax.swing.JLabel import javax.swing.JPanel -class IAmAFirstRobotics( - parent: JFrame, - visualizer: Visualizer -) { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class IAmAFirstRobotics : KoinComponent { + + private val visualizer: Visualizer by inject() + + private val dialog = JDialog(visualizer.frame).apply { - private val dialog = JDialog(parent).apply { isModal = true title = "Welcome !" contentPane.layout = GridBagLayout() @@ -100,9 +103,10 @@ class IAmAFirstRobotics( add(JButton("Next").apply { addActionListener { - dialog.dispose() // Close the dialog on click - IAmAPaperVision(parent, visualizer) + dialog.dispose() + IAmAPaperVision() } + }) border = BorderFactory.createEmptyBorder(0, 10, 10, 10) 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 index 35a08de7..43f0c9e1 100644 --- 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 @@ -37,12 +37,15 @@ import javax.swing.JFrame import javax.swing.JLabel import javax.swing.JPanel -class IAmAGeneralPublic( - parent: JFrame, - visualizer: Visualizer -) { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class IAmAGeneralPublic : KoinComponent { + + private val visualizer: Visualizer by inject() + + private val dialog = JDialog(visualizer.frame).apply { - private val dialog = JDialog(parent).apply { isModal = true title = "Welcome !" contentPane.layout = GridBagLayout() @@ -96,15 +99,14 @@ class IAmAGeneralPublic( } }) - 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) + dialog.dispose() + IAmAPaperVision() } }) + border = BorderFactory.createEmptyBorder(0, 10, 10, 10) } 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 index fa62de54..16ed03b3 100644 --- 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 @@ -25,6 +25,9 @@ package com.github.serivesmejia.eocvsim.gui.dialog.iama import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.Visualizer +import com.github.serivesmejia.eocvsim.config.ConfigManager +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import java.awt.BorderLayout import java.awt.Dimension import javax.swing.BorderFactory @@ -40,17 +43,21 @@ import javax.swing.JPanel import javax.swing.SwingConstants class IAmAPaperVision( - parent: JFrame, - visualizer: Visualizer, - specificallyInterested: Boolean = false, - showWorkspacesButton: Boolean = true -) { + private val specificallyInterested: Boolean = false, + private val showWorkspacesButton: Boolean = true +) : KoinComponent { companion object { val papervisionGif = ImageIcon(this::class.java.getResource("/images/papervision.gif")) } - val dialog = JDialog(parent).apply { + private val visualizer: Visualizer by inject() + private val configManager: ConfigManager by inject() + + private val dialogFactory: DialogFactory by inject() + + val dialog = JDialog(visualizer.frame).apply { + isModal = true title = "Welcome !" @@ -115,7 +122,7 @@ class IAmAPaperVision( add(JButton("Use Workspaces Instead").apply { addActionListener { dialog.dispose() // Close the dialog on click - DialogFactory.createWorkspace(visualizer) + dialogFactory.createWorkspace() } }) @@ -131,7 +138,8 @@ class IAmAPaperVision( visualizer.sidebarPanel.selectedIndex = indexOfTab } else { JOptionPane.showMessageDialog( - parent, + visualizer.frame, + "PaperVision is not currently available, please check your plugin settings.", "Warning", JOptionPane.ERROR_MESSAGE @@ -140,14 +148,15 @@ class IAmAPaperVision( } fun openPaperVisionByDefault() { - visualizer.eocvSim.config.flags["prefersPaperVision"] = true + configManager.config.flags["prefersPaperVision"] = true } if(specificallyInterested) { openPaperVisionByDefault() JOptionPane.showConfirmDialog( - parent, + visualizer.frame, + "From now on, EOCV-Sim will focus on PaperVision upon startup.\nYou can change this in the settings.", "PaperVision", JOptionPane.DEFAULT_OPTION, @@ -155,7 +164,8 @@ class IAmAPaperVision( ) } else { JOptionPane.showOptionDialog( - parent, + visualizer.frame, + "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, @@ -167,7 +177,7 @@ class IAmAPaperVision( if(it == JOptionPane.YES_OPTION) { openPaperVisionByDefault() } else { - visualizer.eocvSim.config.flags["prefersPaperVision"] = false + configManager.config.flags["prefersPaperVision"] = false } } } @@ -181,7 +191,7 @@ class IAmAPaperVision( dialog.contentPane.add(buttonsPanel) - visualizer.eocvSim.config.flags["hasShownIamPaperVision"] = true + configManager.config.flags["hasShownIamPaperVision"] = true dialog.isVisible = true } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt index ac9b4e5f..eabc9e24 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt @@ -22,7 +22,11 @@ package com.github.serivesmejia.eocvsim.gui.dialog.source -import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.config.ConfigManager +import com.github.serivesmejia.eocvsim.input.InputSourceManager +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.gui.Visualizer +import org.koin.core.qualifier.named import com.github.serivesmejia.eocvsim.gui.component.input.EnumComboBox import com.github.serivesmejia.eocvsim.gui.util.WebcamDriver import com.github.serivesmejia.eocvsim.input.source.CameraSource @@ -39,28 +43,35 @@ import org.opencv.core.Mat import org.opencv.core.Size import java.awt.* import javax.swing.* +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject -class CreateCameraSource( - parent: JFrame, - private val eocvSim: EOCVSim -) { +class CreateCameraSource : KoinComponent { + + private val inputSourceManager: InputSourceManager by inject() + private val configManager: ConfigManager by inject() + private val onMainUpdate: EventHandler by inject(named("onMainLoop")) + + private val visualizer: Visualizer by inject() companion object { const val VISIBLE_CHARACTERS_COMBO_BOX = 22 private val sizes = mutableMapOf>() } - val createCameraSource = JDialog(parent) + val createCameraSource = JDialog(visualizer.frame) private val statusLabel = JLabel("", SwingConstants.CENTER) private val camerasComboBox = JComboBox() private val dimensionsComboBox = JComboBox() + private val rotationComboBox = EnumComboBox( "", WebcamRotation::class.java, - WebcamRotation.entries.toTypedArray(), // Correction: Using .entries + WebcamRotation.entries.toTypedArray(), WebcamRotation::displayName - ) { WebcamRotation.fromDisplayName(it) ?: WebcamRotation.UPRIGHT } // Correction: Lambda moved out + ) { WebcamRotation.fromDisplayName(it) ?: WebcamRotation.UPRIGHT } + private val nameTextField = JTextField(15) private val createButton = JButton() private var wasCancelled = false @@ -73,13 +84,12 @@ class CreateCameraSource( private enum class State { INITIAL, CLICKED_TEST, TEST_SUCCESSFUL, TEST_FAILED, NO_WEBCAMS, UNSUPPORTED } init { - eocvSim.visualizer.childDialogs.add(createCameraSource) - // Force preferred driver fallback - if (eocvSim.config.preferredWebcamDriver == WebcamDriver.OpenIMAJ) - eocvSim.config.preferredWebcamDriver = WebcamDriver.OpenPnp + if (configManager.config.preferredWebcamDriver == WebcamDriver.OpenIMAJ) + configManager.config.preferredWebcamDriver = WebcamDriver.OpenPnp + + val preferredDriver = configManager.config.preferredWebcamDriver - val preferredDriver = eocvSim.config.preferredWebcamDriver when (preferredDriver) { WebcamDriver.OpenPnp -> { @@ -151,7 +161,8 @@ class CreateCameraSource( gbc.gridx = 0; gbc.gridy = 1 add(JLabel("Source name: ", JLabel.RIGHT), gbc) gbc.gridx = 1 - nameTextField.text = "CameraSource-${eocvSim.inputSourceManager.sources.size + 1}" + nameTextField.text = "CameraSource-${inputSourceManager.sources.size + 1}" + add(nameTextField, gbc) // Suggested resolution @@ -247,7 +258,8 @@ class CreateCameraSource( return } - nameTextField.text = eocvSim.inputSourceManager.tryName(webcam.name) + nameTextField.text = inputSourceManager.tryName(webcam.name) + dimensionsComboBox.removeAllItems() CoroutineScope(Dispatchers.IO).launch { @@ -324,18 +336,21 @@ class CreateCameraSource( } private fun createSource(name: String, camName: String, size: Size, rotation: WebcamRotation) { - eocvSim.onMainUpdate.once { eocvSim.inputSourceManager.addInputSource(name, CameraSource(camName, size, rotation), true) } + onMainUpdate.once { inputSourceManager.addInputSource(name, CameraSource(camName, size, rotation), true) } } + private fun createSource(name: String, camIndex: Int, size: Size, rotation: WebcamRotation) { - eocvSim.onMainUpdate.once { eocvSim.inputSourceManager.addInputSource(name, CameraSource(camIndex, size, rotation), true) } + onMainUpdate.once { inputSourceManager.addInputSource(name, CameraSource(camIndex, size, rotation), true) } } + private fun updateCreateButton() { createButton.isEnabled = nameTextField.text.trim().isNotEmpty() && - !eocvSim.inputSourceManager.isNameInUse(nameTextField.text) + !inputSourceManager.isNameInUse(nameTextField.text) } + private fun getSelectedIndex() = indexes[camerasComboBox.selectedItem] ?: 0 // Simple helper for DocumentListener in Kotlin diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateHttpSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateHttpSource.kt index eda30e10..d8eb07ab 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateHttpSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateHttpSource.kt @@ -23,7 +23,10 @@ package com.github.serivesmejia.eocvsim.gui.dialog.source import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.gui.Visualizer +import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.github.serivesmejia.eocvsim.input.source.HttpSource +import com.github.serivesmejia.eocvsim.util.event.EventHandler import java.awt.BorderLayout import java.awt.FlowLayout import java.awt.GridBagConstraints @@ -33,16 +36,23 @@ import javax.swing.* import javax.swing.event.DocumentEvent import javax.swing.event.DocumentListener -class CreateHttpSource(parent: JFrame, private val eocvSim: EOCVSim) { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named + +class CreateHttpSource : KoinComponent { + + private val inputSourceManager: InputSourceManager by inject() + private val onMainLoop: EventHandler by inject(named("onMainLoop")) + private val visualizer: Visualizer by inject() + + private val dialog = JDialog(visualizer.frame) - private val dialog = JDialog(parent) private val nameTextField: JTextField private val urlField: JTextField private val createButton: JButton init { - eocvSim.visualizer.childDialogs.add(dialog) - // Panel for input fields val fieldsPanel = JPanel(GridBagLayout()).apply { border = BorderFactory.createEmptyBorder(0, 0, 10, 0) @@ -62,7 +72,7 @@ class CreateHttpSource(parent: JFrame, private val eocvSim: EOCVSim) { gbc.gridx = 0 add(JLabel("Source name: "), gbc) gbc.gridx = 1 - val sourceCount = eocvSim.inputSourceManager.sources.size + 1 + val sourceCount = inputSourceManager.sources.size + 1 nameTextField = JTextField("HttpSource-$sourceCount", 15) add(nameTextField, gbc) } @@ -111,8 +121,8 @@ class CreateHttpSource(parent: JFrame, private val eocvSim: EOCVSim) { } private fun createSource(sourceName: String, url: String) { - eocvSim.onMainUpdate.once { - eocvSim.inputSourceManager.addInputSource( + onMainLoop.once { + inputSourceManager.addInputSource( sourceName, HttpSource(url), false @@ -121,8 +131,7 @@ class CreateHttpSource(parent: JFrame, private val eocvSim: EOCVSim) { } private fun updateCreateButton() { - val isNameValid = nameTextField.text.isNotBlank() && - !eocvSim.inputSourceManager.isNameInUse(nameTextField.text) + val isNameValid = nameTextField.text.isNotBlank() && !inputSourceManager.isNameInUse(nameTextField.text) val isUrlValid = urlField.text.isNotBlank() createButton.isEnabled = isNameValid && isUrlValid } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt index 97613e24..2b46c743 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt @@ -23,12 +23,15 @@ package com.github.serivesmejia.eocvsim.gui.dialog.source import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.gui.component.input.FileSelector import com.github.serivesmejia.eocvsim.gui.component.input.SizeFields +import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.github.serivesmejia.eocvsim.input.source.ImageSource import io.github.deltacv.vision.external.util.CvUtil import com.github.serivesmejia.eocvsim.util.FileFilters import com.github.serivesmejia.eocvsim.util.StrUtil +import com.github.serivesmejia.eocvsim.util.event.EventHandler import org.opencv.core.Size import java.awt.* import java.io.File @@ -37,13 +40,19 @@ import javax.swing.event.DocumentEvent import javax.swing.event.DocumentListener import kotlin.math.roundToInt +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named + class CreateImageSource( - parent: JFrame, - private val eocvSim: EOCVSim, private val initialFile: File? -) { +) : KoinComponent { + + private val visualizer: Visualizer by inject() + private val inputSourceManager: InputSourceManager by inject() + private val onMainLoop: EventHandler by inject(named("onMainLoop")) - val createImageSource = JDialog(parent) + val createImageSource = JDialog(visualizer.frame) val nameTextField = JTextField() val sizeFieldsInput = SizeFields() @@ -53,7 +62,6 @@ class CreateImageSource( private var selectedValidImage = false init { - eocvSim.visualizer.childDialogs.add(createImageSource) initCreateImageSource() } @@ -84,7 +92,7 @@ class CreateImageSource( val namePanel = JPanel(FlowLayout()) val nameLabel = JLabel("Source name: ").apply { horizontalAlignment = JLabel.LEFT } - nameTextField.text = "ImageSource-${eocvSim.inputSourceManager.sources.size + 1}" + nameTextField.text = "ImageSource-${inputSourceManager.sources.size + 1}" namePanel.add(nameLabel) namePanel.add(nameTextField) @@ -140,7 +148,7 @@ class CreateImageSource( if (CvUtil.checkImageValid(fileAbsPath)) { val fileName = StrUtil.getFileBaseName(f.name) if (fileName.isNotBlank()) { - nameTextField.text = eocvSim.inputSourceManager.tryName(fileName) + nameTextField.text = inputSourceManager.tryName(fileName) } val size = CvUtil.scaleToFit(CvUtil.getImageSize(fileAbsPath), EOCVSim.DEFAULT_EOCV_SIZE) @@ -162,8 +170,8 @@ class CreateImageSource( } private fun createSource(sourceName: String, imgPath: String, size: Size) { - eocvSim.onMainUpdate.once { - eocvSim.inputSourceManager.addInputSource( + onMainLoop.once { + inputSourceManager.addInputSource( sourceName, ImageSource(imgPath, size), false @@ -176,6 +184,6 @@ class CreateImageSource( nameTextField.text.trim().isNotEmpty() && sizeFieldsInput.valid && selectedValidImage && - !eocvSim.inputSourceManager.isNameInUse(nameTextField.text) + !inputSourceManager.isNameInUse(nameTextField.text) } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateSource.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateSource.java deleted file mode 100644 index 8e49be58..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateSource.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.gui.dialog.source; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.gui.DialogFactory; -import com.github.serivesmejia.eocvsim.input.SourceType; - -import javax.swing.*; -import java.awt.*; - -public class CreateSource { - - public static volatile boolean alreadyOpened = false; - public volatile JDialog chooseSource = null; - - EOCVSim eocvSim = null; - - private volatile JFrame parent = null; - - public CreateSource(JFrame parent, EOCVSim eocvSim) { - chooseSource = new JDialog(parent); - - this.parent = parent; - this.eocvSim = eocvSim; - - initChooseSource(); - } - - private void initChooseSource() { - - alreadyOpened = true; - - chooseSource.setModal(false); - chooseSource.setFocusableWindowState(false); - chooseSource.setAlwaysOnTop(true); - - chooseSource.setTitle("Select source type"); - chooseSource.setSize(300, 150); - - JPanel contentsPane = new JPanel(new GridLayout(2, 1)); - - JPanel dropDownPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); - - SourceType[] sourceTypes = SourceType.values(); - String[] sourceTypesStr = new String[sourceTypes.length - 1]; - - for (int i = 0; i < sourceTypes.length - 1; i++) { - sourceTypesStr[i] = sourceTypes[i].coolName; - } - - JComboBox dropDown = new JComboBox<>(sourceTypesStr); - dropDownPanel.add(dropDown); - contentsPane.add(dropDownPanel); - - JPanel buttonsPanel = new JPanel(new FlowLayout()); - JButton nextButton = new JButton("Next"); - - buttonsPanel.add(nextButton); - - JButton cancelButton = new JButton("Cancel"); - buttonsPanel.add(cancelButton); - - contentsPane.add(buttonsPanel); - - contentsPane.setBorder(BorderFactory.createEmptyBorder(15, 0, 0, 0)); - - chooseSource.getContentPane().add(contentsPane, BorderLayout.CENTER); - - cancelButton.addActionListener(e -> close()); - - nextButton.addActionListener(e -> { - close(); - SourceType sourceType = SourceType.fromCoolName(dropDown.getSelectedItem().toString()); - DialogFactory.createSourceDialog(eocvSim, sourceType); - }); - - chooseSource.pack(); - chooseSource.setLocationRelativeTo(null); - chooseSource.setResizable(false); - chooseSource.setVisible(true); - } - - public void close() { - alreadyOpened = false; - chooseSource.setVisible(false); - chooseSource.dispose(); - } - -} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateSourceEx.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateSourceEx.kt index bf38b3be..eda991f8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateSourceEx.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateSourceEx.kt @@ -41,12 +41,16 @@ import javax.swing.JFrame import javax.swing.JLabel import javax.swing.JPanel -class CreateSourceEx( - val parent: JFrame, - val visualizer: Visualizer -) { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +class CreateSourceEx : KoinComponent { + + private val visualizer: Visualizer by inject() + private val dialogFactory: DialogFactory by inject() + + val dialog = JDialog(visualizer.frame) - val dialog = JDialog(parent) init { dialog.isModal = true @@ -67,7 +71,7 @@ class CreateSourceEx( addActionListener { dialog.dispose() - DialogFactory.createSourceDialog(visualizer.eocvSim, SourceType.CAMERA) + dialogFactory.createSourceDialog(SourceType.CAMERA) } }) @@ -81,7 +85,7 @@ class CreateSourceEx( addActionListener { dialog.dispose() - DialogFactory.createSourceDialog(visualizer.eocvSim, SourceType.IMAGE) + dialogFactory.createSourceDialog(SourceType.IMAGE) } }) @@ -95,7 +99,7 @@ class CreateSourceEx( addActionListener { dialog.dispose() - DialogFactory.createSourceDialog(visualizer.eocvSim, SourceType.VIDEO) + dialogFactory.createSourceDialog(SourceType.VIDEO) } }) @@ -109,7 +113,7 @@ class CreateSourceEx( addActionListener { dialog.dispose() - DialogFactory.createSourceDialog(visualizer.eocvSim, SourceType.HTTP) + dialogFactory.createSourceDialog(SourceType.HTTP) } }) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt index 54441464..031e8a08 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt @@ -23,10 +23,13 @@ package com.github.serivesmejia.eocvsim.gui.dialog.source import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.gui.component.input.FileSelector import com.github.serivesmejia.eocvsim.gui.component.input.SizeFields +import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.github.serivesmejia.eocvsim.input.source.VideoSource import com.github.serivesmejia.eocvsim.util.FileFilters +import com.github.serivesmejia.eocvsim.util.event.EventHandler import io.github.deltacv.vision.external.util.CvUtil import org.opencv.core.Size import java.awt.BorderLayout @@ -38,12 +41,20 @@ import javax.swing.event.DocumentEvent import javax.swing.event.DocumentListener import kotlin.math.roundToInt +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named + class CreateVideoSource( - parent: JFrame, - private val eocvsim: EOCVSim, - initialFile: File? = null -) { - private val dialog: JDialog = JDialog(parent) + private val initialFile: File? = null +) : KoinComponent { + + private val visualizer: Visualizer by inject() + private val inputSourceManager by inject() + private val onMainLoop: EventHandler by inject(named("onMainLoop")) + + private val dialog: JDialog = JDialog(visualizer.frame) + private var nameTextField: JTextField private var fileSelector: FileSelector @@ -53,8 +64,6 @@ class CreateVideoSource( private var selectedValidVideo = false init { - eocvsim.visualizer.childDialogs.add(dialog) - // Main content panel val contentsPanel = JPanel(GridLayout(4, 1)).apply { border = BorderFactory.createEmptyBorder(15, 0, 0, 0) @@ -79,7 +88,7 @@ class CreateVideoSource( // Name input panel val namePanel = JPanel(FlowLayout()).apply { - val sourceCount = eocvsim.inputSourceManager.sources.size + 1 + val sourceCount = inputSourceManager.sources.size + 1 nameTextField = JTextField("VideoSource-$sourceCount", 15) add(JLabel("Source name: ")) add(nameTextField) @@ -122,7 +131,7 @@ class CreateVideoSource( // Suggest a name from the filename if it's not blank val fileName = file.nameWithoutExtension if (fileName.isNotBlank()) { - nameTextField.text = eocvsim.inputSourceManager.tryName(fileName) + nameTextField.text = inputSourceManager.tryName(fileName) } // Calculate a fitted size and update the fields @@ -148,8 +157,8 @@ class CreateVideoSource( } private fun createSource(sourceName: String, videoPath: String, size: Size) { - eocvsim.onMainUpdate.once { - eocvsim.inputSourceManager.addInputSource( + onMainLoop.once { + inputSourceManager.addInputSource( sourceName, VideoSource(videoPath, size), true @@ -159,7 +168,7 @@ class CreateVideoSource( private fun updateCreateButton() { val isNameValid = nameTextField.text.isNotBlank() && - !eocvsim.inputSourceManager.isNameInUse(nameTextField.text) + !inputSourceManager.isNameInUse(nameTextField.text) createButton.isEnabled = isNameValid && sizeFields.valid && selectedValidVideo } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/GuiUtil.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/GuiUtil.java deleted file mode 100644 index 73ea2b3b..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/GuiUtil.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.gui.util; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.github.serivesmejia.eocvsim.gui.DialogFactory; -import com.github.serivesmejia.eocvsim.gui.dialog.FileAlreadyExists; -import io.github.deltacv.vision.external.util.CvUtil; -import com.github.serivesmejia.eocvsim.util.SysUtil; -import org.opencv.core.Mat; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.imageio.ImageIO; -import javax.swing.*; -import javax.swing.filechooser.FileNameExtensionFilter; -import javax.swing.text.AbstractDocument; -import javax.swing.text.AttributeSet; -import javax.swing.text.BadLocationException; -import javax.swing.text.DocumentFilter; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; -import java.nio.charset.Charset; -import java.util.Arrays; -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public final class GuiUtil { - - static Logger logger = LoggerFactory.getLogger(GuiUtil.class); - - public static void jTextFieldOnlyNumbers(JTextField field, int minNumber, int onMinNumberChangeTo) { - - ((AbstractDocument) field.getDocument()).setDocumentFilter(new DocumentFilter() { - final Pattern regEx = Pattern.compile("\\d*"); - - @Override - public void replace(FilterBypass fb, int offset, int length, String text, AttributeSet attrs) throws BadLocationException { - Matcher matcher = regEx.matcher(text); - if (!matcher.matches()) { - return; - } - - if (field.getText().length() == 0) { - try { - int number = Integer.parseInt(text); - if (number <= minNumber) { - text = String.valueOf(onMinNumberChangeTo); - } - } catch (NumberFormatException ex) { - } - } - - super.replace(fb, offset, length, text, attrs); - } - }); - - } - - public static ImageIcon loadImageIcon(String path) throws IOException { - return new ImageIcon(loadBufferedImage(path)); - } - - public static BufferedImage loadBufferedImage(String path) throws IOException { - return ImageIO.read(GuiUtil.class.getResourceAsStream(path)); - } - - public static void saveBufferedImage(File file, BufferedImage bufferedImage, String format) throws IOException { - ImageIO.write(bufferedImage, format, file); - } - - public static void saveBufferedImage(File file, BufferedImage bufferedImage) throws IOException { - saveBufferedImage(file, bufferedImage, "jpg"); - } - - public static void catchSaveBufferedImage(File file, BufferedImage bufferedImage, String format) { - try { - saveBufferedImage(file, bufferedImage, format); - } catch (IOException e) { - logger.error("Failed to save buffered image", e); - } - } - - public static void catchSaveBufferedImage(File file, BufferedImage bufferedImage) { - catchSaveBufferedImage(file, bufferedImage, "jpg"); - } - - public static void invertBufferedImageColors(BufferedImage input) { - - for (int x = 0; x < input.getWidth(); x++) { - for (int y = 0; y < input.getHeight(); y++) { - - int rgba = input.getRGB(x, y); - Color col = new Color(rgba, true); - - if (col.getAlpha() <= 0) continue; - - col = new Color(255 - col.getRed(), - 255 - col.getGreen(), - 255 - col.getBlue()); - - input.setRGB(x, y, col.getRGB()); - - } - } - - } - - public static void saveBufferedImageFileChooser(Component parent, BufferedImage bufferedImage, EOCVSim eocvSim) { - - FileNameExtensionFilter jpegFilter = new FileNameExtensionFilter("JPEG (*.jpg)", "jpg", "jpeg"); - FileNameExtensionFilter pngFilter = new FileNameExtensionFilter("PNG (*.png)", "png"); - - String[] validExts = {"jpg", "jpeg", "png"}; - - DialogFactory.createFileChooser(parent, DialogFactory.FileChooser.Mode.SAVE_FILE_SELECT, jpegFilter, pngFilter) - - .addCloseListener((MODE, selectedFile, selectedFileFilter) -> { - if (MODE == JFileChooser.APPROVE_OPTION) { - - Optional extension = SysUtil.getExtensionByStringHandling(selectedFile.getName()); - - boolean saveImage; - - if (!selectedFile.exists()) { - saveImage = true; - } else { - FileAlreadyExists.UserChoice userChoice = DialogFactory.createFileAlreadyExistsDialog(eocvSim); //create confirm dialog - saveImage = userChoice == FileAlreadyExists.UserChoice.REPLACE; - } - - String ext = ""; - - if (saveImage) { - if (selectedFileFilter instanceof FileNameExtensionFilter) { //if user selected an extension - - //get selected extension - ext = ((FileNameExtensionFilter) selectedFileFilter).getExtensions()[0]; - - selectedFile = new File(selectedFile + "." + ext); //append extension to file - catchSaveBufferedImage(selectedFile, bufferedImage, ext); - - } else if (extension.isPresent() && Arrays.asList(validExts).contains(extension.get())) { //if user gave a extension to file and it's valid (jpg, jpeg or png) - - ext = extension.get(); //get the extension - catchSaveBufferedImage(selectedFile, bufferedImage, ext); - - } else { //default to jpg if the conditions are not met - - selectedFile = new File(selectedFile + ".jpg"); //append default extension to file - catchSaveBufferedImage(selectedFile, bufferedImage); - - } - } - - } - }); - - } - - public static void saveMatFileChooser(Component parent, Mat mat, EOCVSim eocvSim) { - Mat clonedMat = mat.clone(); - - BufferedImage img = CvUtil.matToBufferedImage(clonedMat); - clonedMat.release(); - - saveBufferedImageFileChooser(parent, img, eocvSim); - } - - public static ListModel isToListModel(InputStream is, Charset charset) throws UnsupportedEncodingException { - - DefaultListModel listModel = new DefaultListModel<>(); - String isStr = SysUtil.loadIsStr(is, charset); - - String[] lines = isStr.split("\n"); - - for(int i = 0 ; i < lines.length ; i++) { - listModel.add(i, lines[i]); - } - - return listModel; - - } - - public static class NoSelectionModel extends DefaultListSelectionModel { - - @Override - public void setAnchorSelectionIndex(final int anchorIndex) {} - - @Override - public void setLeadAnchorNotificationEnabled(final boolean flag) {} - - @Override - public void setLeadSelectionIndex(final int leadIndex) {} - - @Override - public void setSelectionInterval(final int index0, final int index1) { } - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/GuiUtil.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/GuiUtil.kt new file mode 100644 index 00000000..a0e828c5 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/GuiUtil.kt @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2021 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.gui.util + +import com.github.serivesmejia.eocvsim.gui.DialogFactory +import com.github.serivesmejia.eocvsim.gui.dialog.FileAlreadyExists +import com.github.serivesmejia.eocvsim.util.SysUtil +import io.github.deltacv.common.util.loggerForThis +import io.github.deltacv.vision.external.util.CvUtil +import org.opencv.core.Mat +import org.slf4j.LoggerFactory +import java.awt.Color +import java.awt.Component +import java.awt.image.BufferedImage +import java.io.File +import java.io.IOException +import java.io.InputStream +import java.nio.charset.Charset +import javax.imageio.ImageIO +import javax.swing.* +import javax.swing.filechooser.FileNameExtensionFilter + +object GuiUtil { + + private val logger = LoggerFactory.getLogger(GuiUtil::class.java) + + @JvmStatic + fun loadBufferedImage(path: String): BufferedImage { + return ImageIO.read(javaClass.getResourceAsStream(path) ?: throw IOException("Resource not found: $path")) + } + + @JvmStatic + fun saveBufferedImage(file: File, bufferedImage: BufferedImage, format: String = "jpg") { + ImageIO.write(bufferedImage, format, file) + } + + @JvmStatic + fun catchSaveBufferedImage(file: File, bufferedImage: BufferedImage, format: String = "jpg") { + try { + saveBufferedImage(file, bufferedImage, format) + } catch (e: IOException) { + logger.error("Failed to save buffered image", e) + } + } + + @JvmStatic + fun invertBufferedImageColors(input: BufferedImage) { + for (x in 0 until input.width) { + for (y in 0 until input.height) { + val rgba = input.getRGB(x, y) + var col = Color(rgba, true) + + if (col.alpha <= 0) continue + + col = Color( + 255 - col.red, + 255 - col.green, + 255 - col.blue + ) + + input.setRGB(x, y, col.rgb) + } + } + } + + @JvmStatic + fun saveBufferedImageFileChooser(parent: Component?, bufferedImage: BufferedImage, dialogFactory: DialogFactory) { + val jpegFilter = FileNameExtensionFilter("JPEG (*.jpg)", "jpg", "jpeg") + val pngFilter = FileNameExtensionFilter("PNG (*.png)", "png") + + val validExts = arrayOf("jpg", "jpeg", "png") + + dialogFactory.createFileChooser(parent, DialogFactory.FileChooser.Mode.SAVE_FILE_SELECT, jpegFilter, pngFilter) + .addCloseListener { mode, selectedFile, selectedFileFilter -> + if (mode == JFileChooser.APPROVE_OPTION && selectedFile != null) { + val extension = SysUtil.getExtensionByStringHandling(selectedFile.name) + + var saveImage: Boolean + + if (!selectedFile.exists()) { + saveImage = true + } else { + val userChoice = dialogFactory.createFileAlreadyExistsDialog() + saveImage = userChoice == FileAlreadyExists.UserChoice.REPLACE + } + + if (saveImage) { + var fileToSave = selectedFile + var ext = "" + + if (selectedFileFilter is FileNameExtensionFilter) { + ext = selectedFileFilter.extensions[0] + fileToSave = File(selectedFile.absolutePath + "." + ext) + catchSaveBufferedImage(fileToSave, bufferedImage, ext) + } else if (extension.isPresent && validExts.contains(extension.get())) { + ext = extension.get() + catchSaveBufferedImage(fileToSave, bufferedImage, ext) + } else { + fileToSave = File(selectedFile.absolutePath + ".jpg") + catchSaveBufferedImage(fileToSave, bufferedImage) + } + } + } + } + } + + @JvmStatic + fun saveMatFileChooser(parent: Component?, mat: Mat, dialogFactory: DialogFactory) { + val clonedMat = mat.clone() + val img = CvUtil.matToBufferedImage(clonedMat) + clonedMat.release() + + saveBufferedImageFileChooser(parent, img, dialogFactory) + } + + class NoSelectionModel : DefaultListSelectionModel() { + override fun setAnchorSelectionIndex(anchorIndex: Int) {} + override fun setLeadAnchorNotificationEnabled(flag: Boolean) {} + override fun setLeadSelectionIndex(leadIndex: Int) {} + override fun setSelectionInterval(index0: Int, index1: Int) {} + } + + @JvmStatic + fun isToListModel(inputStream: InputStream, charset: Charset): ListModel { + val listModel = DefaultListModel() + val isStr = SysUtil.loadIsStr(inputStream, charset) + val lines = isStr.split("\n").toTypedArray() + + for (i in lines.indices) { + listModel.add(i, lines[i]) + } + + return listModel + } +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.java deleted file mode 100644 index 1a320bd6..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.input; - -import com.github.serivesmejia.eocvsim.EOCVSim; -import com.google.gson.annotations.Expose; -import org.opencv.core.Mat; -import org.opencv.core.Size; - -import javax.swing.filechooser.FileFilter; - -public abstract class InputSource implements Comparable { - - public transient boolean isDefault; - public transient EOCVSim eocvSim; - - protected transient String name = ""; - protected transient boolean isPaused; - private transient boolean beforeIsPaused; - - @Expose - protected long createdOn = -1L; - - public abstract boolean init(); - public abstract void reset(); - - public void cleanIfDirty() { } - - public abstract void close(); - - public abstract void onPause(); - public abstract void onResume(); - - public void setSize(Size size) {} - - public Size getSize() { return new Size(); } - - public Mat update() { - return null; - } - - public final InputSource cloneSource() { - final InputSource source = internalCloneSource(); - source.createdOn = createdOn; - return source; - } - - protected abstract InputSource internalCloneSource(); - - public final void setPaused(boolean paused) { - - isPaused = paused; - - if (beforeIsPaused != isPaused) { - if (isPaused) { - onPause(); - } else { - onResume(); - } - } - - beforeIsPaused = paused; - - } - - public boolean getPaused() { - return isPaused; - } - - public final String getName() { - return name; - } - - public abstract FileFilter getFileFilters(); - - public abstract long getCaptureTimeNanos(); - - public long getCreationTime() { - return createdOn; - } - - @Override - public final int compareTo(InputSource source) { - return createdOn > source.createdOn ? 1 : -1; - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt new file mode 100644 index 00000000..edf8b565 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2021 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.input + +import com.google.gson.annotations.Expose +import org.koin.core.component.KoinComponent +import org.opencv.core.Mat +import org.opencv.core.Size +import javax.swing.filechooser.FileFilter + +abstract class InputSource : Comparable, KoinComponent { + + @Transient var isDefault = false + + @Transient open var name: String = "" + + @Transient private var beforeIsPaused = false + @Transient @set:JvmName("setPausedValue") var isPaused = false + set(value) { + field = value + if (beforeIsPaused != value) { + if (value) onPause() else onResume() + } + beforeIsPaused = value + } + + @Expose + @JvmField + var createdOn = -1L + + abstract fun init(): Boolean + abstract fun reset() + + open fun cleanIfDirty() {} + + abstract fun close() + + abstract fun onPause() + abstract fun onResume() + + open fun setSize(size: Size) {} + + open fun getSize() = Size() + + open fun update(): Mat? = null + + fun cloneSource(): InputSource { + val source = internalCloneSource() + source.createdOn = createdOn + return source + } + + protected abstract fun internalCloneSource(): InputSource + + abstract val fileFilters: FileFilter? + abstract val captureTimeNanos: Long + + val creationTime: Long get() = createdOn + + override fun compareTo(other: InputSource): Int { + return if (createdOn > other.createdOn) 1 else -1 + } + +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt index 3001da93..52726549 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt @@ -1,84 +1,99 @@ -package com.github.serivesmejia.eocvsim.input - -import io.github.deltacv.common.util.loggerForThis -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withTimeout -import javax.swing.JDialog - -object InputSourceInitializer { - - const val TIMEOUT = 10000L - - val logger by loggerForThis() - - @OptIn(DelicateCoroutinesApi::class) - fun initializeWithTimeout(inputSource: InputSource, manager: InputSourceManager? = null): Boolean { - var result = false - - val scope = manager?.eocvSim?.scope ?: GlobalScope - - val job = scope.launch { - try { - result = inputSource.init() - } catch (e: Exception) { - logger.error("Error initializing InputSource", e) - } - } - - val dialog = manager?.showApwdIfNeeded(inputSource.name, job) - - runBlocking { - try { - withTimeout(TIMEOUT) { - job.join() - } - } catch (e: CancellationException) { - logger.error("InputSource initialization timed out after $TIMEOUT ms", e) - } finally { - job.cancel() - dialog?.dispose() - } - } - - return result - } - - - @OptIn(DelicateCoroutinesApi::class) - @JvmOverloads - fun runWithTimeout(sourceName: String, manager: InputSourceManager? = null, callback: () -> Boolean): Boolean { - var result = false - - val scope = manager?.eocvSim?.scope ?: GlobalScope - - val job = scope.launch { - try { - result = callback() - } catch (e: Exception) { - logger.error("Error running InputSource", e) - } - } - - val dialog = manager?.showApwdIfNeeded(sourceName, job) - - runBlocking { - try { - withTimeout(TIMEOUT) { - job.join() - } - } catch (e: CancellationException) { - logger.error("InputSource run timed out after $TIMEOUT ms", e) - } finally { - job.cancel() - dialog?.dispose() - } - } - - return result - } - +package com.github.serivesmejia.eocvsim.input + +import io.github.deltacv.common.util.loggerForThis +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeout +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.context.GlobalContext + +class InputSourceInitializer : KoinComponent { + + companion object { + const val TIMEOUT = 10000L + + fun runWithTimeout(sourceName: String, manager: InputSourceManager? = null, callback: () -> Boolean): Boolean { + + val initializer = GlobalContext.get().get() + return initializer.runWithTimeout(sourceName, manager, callback) + } + + fun initializeWithTimeout(inputSource: InputSource): Boolean { + + val initializer = GlobalContext.get().get() + return initializer.initializeWithTimeout(inputSource) + } + } + + private val inputSourceManager: InputSourceManager by inject() + private val scope: CoroutineScope by inject() + + val logger by loggerForThis() + + @OptIn(DelicateCoroutinesApi::class) + fun initializeWithTimeout(inputSource: InputSource): Boolean { + var result = false + + val job = scope.launch { + try { + result = inputSource.init() + } catch (e: Exception) { + logger.error("Error initializing InputSource", e) + } + } + + val dialog = inputSourceManager.showLoadingDialogIfNeeded(inputSource.name, job) + + runBlocking { + try { + withTimeout(TIMEOUT) { + job.join() + } + } catch (e: CancellationException) { + logger.error("InputSource initialization timed out after $TIMEOUT ms", e) + } finally { + job.cancel() + dialog?.dispose() + } + } + + return result + } + + + @OptIn(DelicateCoroutinesApi::class) + @JvmOverloads + fun runWithTimeout(sourceName: String, manager: InputSourceManager? = null, callback: () -> Boolean): Boolean { + var result = false + + val job = scope.launch { + try { + result = callback() + } catch (e: Exception) { + logger.error("Error running InputSource", e) + } + } + + val dialog = manager?.showLoadingDialogIfNeeded(sourceName, job) + + runBlocking { + try { + withTimeout(TIMEOUT) { + job.join() + } + } catch (e: CancellationException) { + logger.error("InputSource run timed out after $TIMEOUT ms", e) + } finally { + job.cancel() + dialog?.dispose() + } + } + + return result + } + } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceLoader.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceLoader.java deleted file mode 100644 index 9db0bff8..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceLoader.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.input; - -import com.github.serivesmejia.eocvsim.input.source.*; -import com.github.serivesmejia.eocvsim.util.SysUtil; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.annotations.Expose; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.util.HashMap; -import java.util.Map; - -public class InputSourceLoader { - - Logger logger = LoggerFactory.getLogger(getClass()); - - public static final Gson gson = new GsonBuilder() - .registerTypeAdapter(CameraSource.class, new CameraSourceAdapter()) - .setPrettyPrinting() - .create(); - - public static final String SOURCES_SAVEFILE_NAME = "eocvsim_sources.json"; - - public static final File SOURCES_SAVEFILE = new File(SysUtil.getEOCVSimFolder() + File.separator + SOURCES_SAVEFILE_NAME); - public static final File SOURCES_SAVEFILE_OLD = new File(SysUtil.getAppData() + File.separator + SOURCES_SAVEFILE_NAME); - - public static final InputSourcesContainer.SourcesFileVersion CURRENT_FILE_VERSION = InputSourcesContainer.SourcesFileVersion.SEIS; - - public HashMap loadedInputSources = new HashMap<>(); - - public InputSourcesContainer.SourcesFileVersion fileVersion = null; - - public void saveInputSource(String name, InputSource source) { - loadedInputSources.put(name, source); - } - - public void deleteInputSource(String name) { - loadedInputSources.remove(name); - } - - public void saveInputSourcesToFile() { - saveInputSourcesToFile(SOURCES_SAVEFILE); - } - - public void saveInputSourcesToFile(File f) { - InputSourcesContainer sourcesContainer = new InputSourcesContainer(); - - //updates file version to most recent since it will be regenerated at this point - if(fileVersion != null) - sourcesContainer.sourcesFileVersion = fileVersion.ordinal() < CURRENT_FILE_VERSION.ordinal() - ? CURRENT_FILE_VERSION : fileVersion; - - for (Map.Entry entry : loadedInputSources.entrySet()) { - if (!entry.getValue().isDefault) { - InputSource source = entry.getValue().cloneSource(); - sourcesContainer.classifySource(entry.getKey(), source); - } - } - - saveInputSourcesToFile(f, sourcesContainer); - } - - public void saveInputSourcesToFile(File file, InputSourcesContainer sourcesContainer) { - String jsonInputSources = gson.toJson(sourcesContainer); - SysUtil.saveFileStr(file, jsonInputSources); - } - - public void saveInputSourcesToFile(InputSourcesContainer sourcesContainer) { - saveInputSourcesToFile(SOURCES_SAVEFILE, sourcesContainer); - } - - public void loadInputSourcesFromFile() { - SysUtil.migrateFile(SOURCES_SAVEFILE_OLD, SOURCES_SAVEFILE); - loadInputSourcesFromFile(SOURCES_SAVEFILE); - } - - public void loadInputSourcesFromFile(File f) { - - if (!f.exists()) return; - - String jsonSources = SysUtil.loadFileStr(f); - if (jsonSources.trim().equals("")) return; - - InputSourcesContainer sources; - - try { - sources = gson.fromJson(jsonSources, InputSourcesContainer.class); - } catch (Exception ex) { - logger.error("Error while parsing sources file, it will be replaced and fixed later on, but the user created sources will be deleted.", ex); - return; - } - - sources.updateAllSources(); - fileVersion = sources.sourcesFileVersion; - - saveInputSourcesToFile(sources); //to make sure version gets declared in case it was an older file - - logger.info("InputSources file version is " + sources.sourcesFileVersion); - - loadedInputSources = sources.allSources; - - } - - static class InputSourcesContainer { - - public transient HashMap allSources = new HashMap<>(); - - public HashMap imageSources = new HashMap<>(); - public HashMap cameraSources = new HashMap<>(); - public HashMap videoSources = new HashMap<>(); - public HashMap httpSources = new HashMap<>(); - - @Expose - public SourcesFileVersion sourcesFileVersion = null; - - public enum SourcesFileVersion { DOS, SEIS, SIETE } - - public void updateAllSources() { - if (sourcesFileVersion == null) sourcesFileVersion = SourcesFileVersion.DOS; - - allSources.clear(); - - allSources.putAll(imageSources); - allSources.putAll(cameraSources); - allSources.putAll(httpSources); - - //check if file version is bigger than DOS, we should have video sources section - //declared in any file with a version greater than that - if (sourcesFileVersion.ordinal() >= 1) { - allSources.putAll(videoSources); - } - } - - public void classifySource(String sourceName, InputSource source) { - switch (SourceType.fromClass(source.getClass())) { - case IMAGE: - imageSources.put(sourceName, (ImageSource) source); - break; - case CAMERA: - cameraSources.put(sourceName, (CameraSource) source); - break; - case VIDEO: - videoSources.put(sourceName, (VideoSource) source); - break; - case HTTP: - httpSources.put(sourceName, (HttpSource) source); - break; - } - } - - } - -} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceLoader.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceLoader.kt new file mode 100644 index 00000000..b70b1773 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceLoader.kt @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2021 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.input + +import com.github.serivesmejia.eocvsim.input.source.* +import com.github.serivesmejia.eocvsim.util.SysUtil +import com.google.gson.Gson +import com.google.gson.GsonBuilder +import com.google.gson.annotations.Expose +import org.slf4j.LoggerFactory +import java.io.File + +class InputSourceLoader { + + private val logger = LoggerFactory.getLogger(javaClass) + + companion object { + @JvmStatic + val gson: Gson = GsonBuilder() + .registerTypeAdapter(CameraSource::class.java, CameraSourceAdapter()) + .setPrettyPrinting() + .create() + + const val SOURCES_SAVEFILE_NAME = "eocvsim_sources.json" + + @JvmStatic + val SOURCES_SAVEFILE = File(SysUtil.getEOCVSimFolder().toString() + File.separator + SOURCES_SAVEFILE_NAME) + @JvmStatic + val SOURCES_SAVEFILE_OLD = File(SysUtil.getAppData().toString() + File.separator + SOURCES_SAVEFILE_NAME) + + @JvmStatic + val CURRENT_FILE_VERSION = InputSourcesContainer.SourcesFileVersion.SEIS + } + + var loadedInputSources = HashMap() + + var fileVersion: InputSourcesContainer.SourcesFileVersion? = null + + fun saveInputSource(name: String, source: InputSource) { + loadedInputSources[name] = source + } + + fun deleteInputSource(name: String) { + loadedInputSources.remove(name) + } + + fun saveInputSourcesToFile() { + saveInputSourcesToFile(SOURCES_SAVEFILE) + } + + fun saveInputSourcesToFile(f: File) { + val sourcesContainer = InputSourcesContainer() + + // updates file version to most recent since it will be regenerated at this point + fileVersion?.let { + sourcesContainer.sourcesFileVersion = if (it.ordinal < CURRENT_FILE_VERSION.ordinal) + CURRENT_FILE_VERSION else it + } + + for ((key, value) in loadedInputSources) { + if (!value.isDefault) { + val source = value.cloneSource() + sourcesContainer.classifySource(key, source) + } + } + + saveInputSourcesToFile(f, sourcesContainer) + } + + fun saveInputSourcesToFile(file: File, sourcesContainer: InputSourcesContainer) { + val jsonInputSources = gson.toJson(sourcesContainer) + SysUtil.saveFileStr(file, jsonInputSources) + } + + fun saveInputSourcesToFile(sourcesContainer: InputSourcesContainer) { + saveInputSourcesToFile(SOURCES_SAVEFILE, sourcesContainer) + } + + fun loadInputSourcesFromFile() { + SysUtil.migrateFile(SOURCES_SAVEFILE_OLD, SOURCES_SAVEFILE) + loadInputSourcesFromFile(SOURCES_SAVEFILE) + } + + fun loadInputSourcesFromFile(f: File) { + if (!f.exists()) return + + val jsonSources = SysUtil.loadFileStr(f) + if (jsonSources.trim() == "") return + + val sources: InputSourcesContainer + try { + sources = gson.fromJson(jsonSources, InputSourcesContainer::class.java) + } catch (ex: Exception) { + logger.error("Error while parsing sources file, it will be replaced and fixed later on, but the user created sources will be deleted.", ex) + return + } + + sources.updateAllSources() + fileVersion = sources.sourcesFileVersion + + saveInputSourcesToFile(sources) // to make sure version gets declared in case it was an older file + + logger.info("InputSources file version is ${sources.sourcesFileVersion}") + + loadedInputSources = sources.allSources + } + + class InputSourcesContainer { + + @Transient var allSources = HashMap() + + var imageSources = HashMap() + var cameraSources = HashMap() + var videoSources = HashMap() + var httpSources = HashMap() + + @Expose + var sourcesFileVersion: SourcesFileVersion? = null + + enum class SourcesFileVersion { DOS, SEIS, SIETE } + + fun updateAllSources() { + if (sourcesFileVersion == null) sourcesFileVersion = SourcesFileVersion.DOS + + allSources.clear() + + allSources.putAll(imageSources) + allSources.putAll(cameraSources) + allSources.putAll(httpSources) + + // check if file version is bigger than DOS, we should have video sources section + // declared in any file with a version greater than that + if (sourcesFileVersion!!.ordinal >= 1) { + allSources.putAll(videoSources) + } + } + + fun classifySource(sourceName: String, source: InputSource) { + when (SourceType.fromClass(source.javaClass)) { + SourceType.IMAGE -> imageSources[sourceName] = source as ImageSource + SourceType.CAMERA -> cameraSources[sourceName] = source as CameraSource + SourceType.VIDEO -> videoSources[sourceName] = source as VideoSource + SourceType.HTTP -> httpSources[sourceName] = source as HttpSource + else -> {} + } + } + } +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt index 6e30faa8..1e32c1bc 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt @@ -23,30 +23,38 @@ package com.github.serivesmejia.eocvsim.input -import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.config.ConfigManager +import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.Visualizer -import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SourceSelectorPanel import com.github.serivesmejia.eocvsim.input.source.ImageSource import com.github.serivesmejia.eocvsim.input.source.NullSource import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler import io.github.deltacv.common.util.loggerForThis +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named import org.opencv.core.Mat import org.opencv.core.Scalar import org.opencv.core.Size import org.opencv.imgproc.Imgproc -import java.awt.Dimension -import java.io.File -import java.io.IOException -import com.github.serivesmejia.eocvsim.gui.DialogFactory import org.openftc.easyopencv.MatRecycler +import java.io.IOException import java.util.concurrent.CancellationException import javax.swing.JDialog -import javax.swing.SwingUtilities -class InputSourceManager(val eocvSim: EOCVSim) { +class InputSourceManager : KoinComponent { + + private val pipelineManager: PipelineManager by inject() + private val configManager: ConfigManager by inject() + private val visualizer: Visualizer by inject() + private val dialogFactory: DialogFactory by inject() + private val onMainLoop: EventHandler by inject(named("onMainLoop")) + + private val inputSourceInitializer: InputSourceInitializer by inject() companion object { private val BLACK = Scalar(0.0, 0.0, 0.0, 255.0) @@ -128,7 +136,7 @@ class InputSourceManager(val eocvSim: EOCVSim) { val currentSource = currentInputSource ?: return try { - currentSource.setPaused(isPaused) + currentSource.isPaused = isPaused val m = currentSource.update() @@ -162,8 +170,6 @@ class InputSourceManager(val eocvSim: EOCVSim) { if (sources.containsKey(name)) return - inputSource.eocvSim = eocvSim - inputSource.name = name sources[name] = inputSource @@ -218,7 +224,7 @@ class InputSourceManager(val eocvSim: EOCVSim) { src?.reset() if (src != null) { - if (!InputSourceInitializer.initializeWithTimeout(src, this)) { + if (!inputSourceInitializer.initializeWithTimeout(src)) { onInputSourceInitError.run() logger.error("Error while loading requested source ($sourceName) reported by itself (init method returned false)") @@ -231,7 +237,7 @@ class InputSourceManager(val eocvSim: EOCVSim) { currentInputSource = src // if pause on images option is turned on by user - if (eocvSim.configManager.config.pauseOnImages) { + if (configManager.config.pauseOnImages) { pauseIfImage() } @@ -264,8 +270,8 @@ class InputSourceManager(val eocvSim: EOCVSim) { // if the new input source is an image, we will pause the next frame // to execute one shot analysis on images and save resources. if (SourceType.fromClass(source.javaClass) == SourceType.IMAGE) { - eocvSim.onMainUpdate.once { - eocvSim.pipelineManager.setPaused( + onMainLoop.once { + pipelineManager.setPaused( true, PipelineManager.PauseReason.IMAGE_ONE_ANALYSIS ) @@ -276,18 +282,18 @@ class InputSourceManager(val eocvSim: EOCVSim) { fun pauseIfImageTwoFrames() { // if the new input source is an image, we will pause the next frame // to execute one shot analysis on images and save resources. - eocvSim.onMainUpdate.once { pauseIfImage() } + onMainLoop.once { pauseIfImage() } } fun requestSetInputSource(name: String?) { - eocvSim.onMainUpdate.once { setInputSource(name) } + onMainLoop.once { setInputSource(name) } } - fun showApwdIfNeeded(sourceName: String?, job: Job?): JDialog? { + fun showLoadingDialogIfNeeded(sourceName: String?, job: Job?): JDialog? { val type = getSourceType(sourceName) if (type == SourceType.CAMERA || type == SourceType.VIDEO || type == SourceType.HTTP) { - return DialogFactory.createInformation( - eocvSim.visualizer.frame, + return dialogFactory.createInformation( + visualizer.frame, "Opening source...", null, "Information", "Cancel" ) { job?.cancel(CancellationException()) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/SourceType.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/SourceType.java deleted file mode 100644 index eaf553c5..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/SourceType.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.input; - -import com.github.serivesmejia.eocvsim.input.source.*; - -import javax.swing.filechooser.FileFilter; -import java.io.File; - -public enum SourceType { - - IMAGE(new ImageSource(""), "Image"), - CAMERA(new CameraSource("", null), "Camera"), - VIDEO(new VideoSource("", null), "Video"), - HTTP(new HttpSource(""), "HTTP"), - UNKNOWN(null, "Unknown"); - - public final Class klazz; - public final String coolName; - public final InputSource stubInstance; - - SourceType(InputSource instance, String coolName) { - stubInstance = instance; - - if(instance != null) - this.klazz = instance.getClass(); - else - this.klazz = null; - - this.coolName = coolName; - } - - public static SourceType fromClass(Class clazz) { - for(SourceType sourceType : values()) { - if(sourceType.klazz == clazz) { - return sourceType; - } - } - return UNKNOWN; - } - - public static SourceType fromCoolName(String coolName) { - for(SourceType sourceType : values()) { - if(sourceType.coolName.equalsIgnoreCase(coolName)) { - return sourceType; - } - } - return UNKNOWN; - } - - public static SourceType isFileUsableForSource(File file) { - for(SourceType type : values()) { - if(type.stubInstance != null && type.stubInstance.getFileFilters() != null) - if(type.stubInstance.getFileFilters().accept(file)) - return type; - } - - return UNKNOWN; - } - -} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/SourceType.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/SourceType.kt new file mode 100644 index 00000000..e78e097d --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/SourceType.kt @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2021 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.input + +import com.github.serivesmejia.eocvsim.input.source.* +import java.io.File + +enum class SourceType(val stubInstance: InputSource?, val coolName: String) { + + IMAGE(ImageSource(), "Image"), + CAMERA(CameraSource(), "Camera"), + VIDEO(VideoSource(), "Video"), + HTTP(HttpSource(), "HTTP"), + UNKNOWN(null, "Unknown"); + + val klazz: Class? = stubInstance?.javaClass + + companion object { + @JvmStatic + fun fromClass(clazz: Class): SourceType { + for (sourceType in entries) { + if (sourceType.klazz == clazz) { + return sourceType + } + } + return UNKNOWN + } + + @JvmStatic + fun fromCoolName(coolName: String): SourceType { + for (sourceType in entries) { + if (sourceType.coolName.equals(coolName, ignoreCase = true)) { + return sourceType + } + } + return UNKNOWN + } + + @JvmStatic + fun isFileUsableForSource(file: File): SourceType { + for (type in entries) { + val filters = type.stubInstance?.fileFilters + if (filters != null && filters.accept(file)) { + return type + } + } + return UNKNOWN + } + } + +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.java deleted file mode 100644 index cf0020cc..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.java +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.input.source; - -import com.github.serivesmejia.eocvsim.gui.Visualizer; -import com.github.serivesmejia.eocvsim.gui.util.WebcamDriver; -import com.github.serivesmejia.eocvsim.input.InputSource; -import com.github.serivesmejia.eocvsim.input.InputSourceInitializer; -import com.github.serivesmejia.eocvsim.util.StrUtil; -import com.google.gson.annotations.Expose; -import io.github.deltacv.steve.Webcam; -import io.github.deltacv.steve.WebcamPropertyControl; -import io.github.deltacv.steve.WebcamRotation; -import io.github.deltacv.steve.opencv.OpenCvWebcam; -import io.github.deltacv.steve.opencv.OpenCvWebcamBackend; -import io.github.deltacv.steve.openpnp.OpenPnpBackend; -import io.github.deltacv.steve.openpnp.OpenPnpWebcam; -import org.opencv.core.Mat; -import org.opencv.core.Size; -import org.opencv.imgproc.Imgproc; -import org.openftc.easyopencv.MatRecycler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.filechooser.FileFilter; -import java.util.List; - -public class CameraSource extends InputSource { - - // for global use, -1 means no webcam currently in use - public static int currentWebcamIndex = -1; - - protected int webcamIndex; - - @Expose - protected String webcamName = null; - - private transient Webcam camera = null; - - private transient MatRecycler.RecyclableMat lastFramePaused = null; - private transient MatRecycler.RecyclableMat lastFrame = null; - - private transient boolean initialized = false; - - protected boolean isLegacyByIndex = false; - - @Expose - protected volatile Size size; - @Expose - protected volatile WebcamRotation rotation; - - private volatile transient MatRecycler matRecycler; - - private transient long capTimeNanos = 0; - - Logger logger = LoggerFactory.getLogger(getClass()); - - public CameraSource() {} - - public CameraSource(String webcamName, Size size, WebcamRotation rotation) { - this.webcamName = webcamName; - this.size = size; - this.rotation = rotation; - } - - public CameraSource(String webcamName, Size size) { - this.webcamName = webcamName; - this.size = size; - this.rotation = WebcamRotation.UPRIGHT; - } - - public CameraSource(int webcamIndex, Size size, WebcamRotation rotation) { - this.webcamIndex = webcamIndex; - this.size = size; - this.rotation = rotation; - isLegacyByIndex = true; - } - - public CameraSource(int webcamIndex, Size size) { - this.webcamIndex = webcamIndex; - this.size = size; - this.rotation = WebcamRotation.UPRIGHT; - isLegacyByIndex = true; - } - - public WebcamPropertyControl getWebcamPropertyControl() { - if(camera == null) return null; - return camera.getPropertyControl(); - } - - public String getWebcamName() { - return webcamName; - } - - @Override - public void setSize(Size size) { - this.size = size; - } - - @Override - public Size getSize() { - return size; - } - - @Override - public boolean init() { - if (initialized) return false; - initialized = true; - - if(rotation == null) rotation = WebcamRotation.UPRIGHT; - - if(webcamName != null) { - if(eocvSim.getConfig().preferredWebcamDriver == WebcamDriver.OpenPnp) { - Webcam.Companion.setBackend(OpenPnpBackend.INSTANCE); - } else if(eocvSim.getConfig().preferredWebcamDriver == WebcamDriver.OpenIMAJ) { - eocvSim.getConfig().preferredWebcamDriver = WebcamDriver.OpenPnp; - Webcam.Companion.setBackend(OpenPnpBackend.INSTANCE); - } - - List webcams = Webcam.Companion.getAvailableWebcams(); - - boolean foundWebcam = false; - - for(Webcam device : webcams) { - String name = device.getName(); - double similarity = StrUtil.similarity(name, webcamName); - - if(name.equals(webcamName) || similarity > 0.6) { - logger.info("\"" + name + "\" compared to \"" + webcamName + "\", similarity " + similarity); - - camera = device; - foundWebcam = true; - break; - } - } - - if(!foundWebcam) { - logger.error("Could not find webcam " + webcamName); - return false; - } - } else { - Webcam.Companion.setBackend(OpenCvWebcamBackend.INSTANCE); - camera = new OpenCvWebcam(webcamIndex, size, rotation); - } - - camera.setResolution(size); - camera.setRotation(rotation); - - try { - camera.open(); - } catch (Exception ex) { - logger.error("Error while opening camera " + webcamIndex, ex); - return false; - } - - if (!camera.isOpen()) { - logger.error("Unable to open camera " + webcamIndex + ", isOpen() returned false."); - return false; - } - - if (matRecycler == null) matRecycler = new MatRecycler(4); - MatRecycler.RecyclableMat newFrame = matRecycler.takeMatOrNull(); - - camera.read(newFrame); - - if (newFrame.empty()) { - logger.error("Unable to open camera " + webcamIndex + ", returned Mat was empty."); - newFrame.release(); - return false; - } - - matRecycler.returnMat(newFrame); - - currentWebcamIndex = webcamIndex; - - return true; - } - - @Override - public void reset() { - if (!initialized) return; - if (camera != null && camera.isOpen()) camera.close(); - - if(lastFrame != null && lastFrame.isCheckedOut()) - lastFrame.returnMat(); - if(lastFramePaused != null && lastFramePaused.isCheckedOut()) - lastFramePaused.returnMat(); - - initialized = false; - } - - @Override - public void close() { - if (camera != null && camera.isOpen()) camera.close(); - currentWebcamIndex = -1; - } - - private MatRecycler.RecyclableMat lastNewFrame = null; - - @Override - public Mat update() { - if(lastNewFrame != null) { - lastNewFrame.returnMat(); - lastNewFrame = null; - } - - if (isPaused) { - return lastFramePaused; - } else if (lastFramePaused != null) { - lastFramePaused.release(); - lastFramePaused.returnMat(); - lastFramePaused = null; - } - - if (lastFrame == null) lastFrame = matRecycler.takeMatOrNull(); - if (camera == null) return lastFrame; - - MatRecycler.RecyclableMat newFrame = matRecycler.takeMatOrNull(); - lastNewFrame = newFrame; - - camera.read(newFrame); - capTimeNanos = System.nanoTime(); - - if (newFrame.empty()) { - newFrame.returnMat(); - return lastFrame; - } - - if (size == null) size = lastFrame.size(); - - newFrame.copyTo(lastFrame); - - newFrame.release(); - newFrame.returnMat(); - - lastNewFrame = null; - - return lastFrame; - } - - @Override - public void onPause() { - if (lastFrame != null) lastFrame.release(); - if (lastFramePaused == null) lastFramePaused = matRecycler.takeMatOrNull(); - - camera.read(lastFramePaused); - Imgproc.cvtColor(lastFramePaused, lastFramePaused, Imgproc.COLOR_BGR2RGB); - - update(); - - camera.close(); - - currentWebcamIndex = -1; - } - - @Override - public void onResume() { - InputSourceInitializer.INSTANCE.runWithTimeout(name, eocvSim.inputSourceManager, () -> { - camera.open(); - return camera.isOpen(); - }); - } - - @Override - protected InputSource internalCloneSource() { - return new CameraSource(webcamName, size); - } - - @Override - public FileFilter getFileFilters() { - return null; - } - - @Override - public long getCaptureTimeNanos() { - return capTimeNanos; - } - - @Override - public String toString() { - if (size == null) size = new Size(); - return "CameraSource(" + webcamName + ", " + webcamIndex + ", " + (size != null ? size.toString() : "null") + ")"; - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt new file mode 100644 index 00000000..e5a6ee36 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2021 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.input.source + +import com.github.serivesmejia.eocvsim.config.ConfigManager +import com.github.serivesmejia.eocvsim.gui.util.WebcamDriver +import com.github.serivesmejia.eocvsim.input.InputSource +import com.github.serivesmejia.eocvsim.input.InputSourceInitializer +import com.github.serivesmejia.eocvsim.input.InputSourceManager +import com.github.serivesmejia.eocvsim.util.StrUtil + +import com.google.gson.annotations.Expose +import io.github.deltacv.steve.Webcam +import io.github.deltacv.steve.WebcamRotation +import io.github.deltacv.steve.opencv.OpenCvWebcam +import io.github.deltacv.steve.opencv.OpenCvWebcamBackend +import io.github.deltacv.steve.openpnp.OpenPnpBackend +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.opencv.core.Mat +import org.opencv.core.Size +import org.opencv.imgproc.Imgproc +import org.openftc.easyopencv.MatRecycler +import org.slf4j.LoggerFactory +import javax.swing.filechooser.FileFilter + +class CameraSource : InputSource { + + + companion object { + // for global use, -1 means no webcam currently in use + @JvmStatic var currentWebcamIndex = -1 + } + + @delegate:Transient + private val configManager: ConfigManager by inject() + @delegate:Transient + private val inputSourceManager: InputSourceManager by inject() + + @Transient var webcamIndex: Int = 0 + + @Expose @JvmField var webcamName: String = "" + + + @Transient private var camera: Webcam? = null + + @Transient private var lastFramePaused: MatRecycler.RecyclableMat? = null + @Transient private var lastFrame: MatRecycler.RecyclableMat? = null + + @Transient private var initialized = false + + @Transient var isLegacyByIndex = false + + @Expose @JvmField @Volatile var size: Size = Size() + @Expose @JvmField @Volatile var rotation: WebcamRotation = WebcamRotation.UPRIGHT + + + @Transient private var matRecycler = MatRecycler(4) + + @Transient private var capTimeNanos: Long = 0 + + @Transient private val logger = LoggerFactory.getLogger(javaClass) + + constructor() : super() { + createdOn = System.currentTimeMillis() + } + + + constructor(webcamName: String?, size: Size?, rotation: WebcamRotation? = WebcamRotation.UPRIGHT) : super() { + this.webcamName = webcamName ?: "" + this.size = size ?: Size() + this.rotation = rotation ?: WebcamRotation.UPRIGHT + createdOn = System.currentTimeMillis() + } + + + constructor(webcamIndex: Int, size: Size?, rotation: WebcamRotation? = WebcamRotation.UPRIGHT) : super() { + this.webcamIndex = webcamIndex + this.size = size ?: Size() + this.rotation = rotation ?: WebcamRotation.UPRIGHT + isLegacyByIndex = true + createdOn = System.currentTimeMillis() + } + + + + fun getWebcamPropertyControl() = camera?.propertyControl + + override fun setSize(size: Size) { + this.size = size + } + + override fun getSize(): Size = size + + + override fun init(): Boolean { + if (initialized) return false + initialized = true + + if (rotation == WebcamRotation.UPRIGHT) rotation = WebcamRotation.UPRIGHT // already defaulted + + + if (webcamName.isNotEmpty()) { + + when (configManager.config.preferredWebcamDriver) { + WebcamDriver.OpenPnp -> Webcam.backend = OpenPnpBackend + WebcamDriver.OpenIMAJ -> { + configManager.config.preferredWebcamDriver = WebcamDriver.OpenPnp + + Webcam.backend = OpenPnpBackend + } + else -> {} + } + + val webcams = Webcam.availableWebcams + var foundWebcam = false + + for (device in webcams) { + val name = device.name + val similarity = StrUtil.similarity(name, webcamName) + + if (name == webcamName || similarity > 0.6) { + logger.info("\"$name\" compared to \"$webcamName\", similarity $similarity") + camera = device + foundWebcam = true + break + } + } + + if (!foundWebcam) { + logger.error("Could not find webcam $webcamName") + return false + } + } else { + Webcam.backend = OpenCvWebcamBackend + camera = OpenCvWebcam(webcamIndex, size, rotation) + + } + + camera?.resolution = size + camera?.rotation = rotation + + + try { + camera?.open() + } catch (ex: Exception) { + logger.error("Error while opening camera $webcamIndex", ex) + return false + } + + if (camera?.isOpen != true) { + logger.error("Unable to open camera $webcamIndex, isOpen() returned false.") + return false + } + + val newFrame = matRecycler.takeMatOrNull() + + camera?.read(newFrame) + + if (newFrame!!.empty()) { + logger.error("Unable to open camera $webcamIndex, returned Mat was empty.") + newFrame.release() + return false + } + + matRecycler.returnMat(newFrame) + currentWebcamIndex = webcamIndex + + return true + } + + override fun reset() { + if (!initialized) return + if (camera?.isOpen == true) camera?.close() + + if (lastFrame?.isCheckedOut == true) lastFrame?.returnMat() + if (lastFramePaused?.isCheckedOut == true) lastFramePaused?.returnMat() + + initialized = false + } + + override fun close() { + if (camera?.isOpen == true) camera?.close() + currentWebcamIndex = -1 + } + + @Transient private var lastNewFrame: MatRecycler.RecyclableMat? = null + + override fun update(): Mat? { + lastNewFrame?.returnMat() + lastNewFrame = null + + if (isPaused) { + return lastFramePaused + } else if (lastFramePaused != null) { + lastFramePaused?.release() + lastFramePaused?.returnMat() + lastFramePaused = null + } + + if (lastFrame == null) lastFrame = matRecycler.takeMatOrNull() + if (camera == null) return lastFrame + + val newFrame = matRecycler.takeMatOrNull() + lastNewFrame = newFrame + + camera?.read(newFrame) + capTimeNanos = System.nanoTime() + + if (newFrame == null || newFrame.empty()) { + + newFrame?.returnMat() + return lastFrame + } + + if (size.area() == 0.0) size = lastFrame!!.size() + + newFrame.copyTo(lastFrame) + + newFrame.release() + newFrame.returnMat() + + lastNewFrame = null + + return lastFrame + } + + override fun onPause() { + if (lastFrame != null) lastFrame?.release() + if (lastFramePaused == null) lastFramePaused = matRecycler.takeMatOrNull() + + camera?.read(lastFramePaused!!) + lastFramePaused?.let { Imgproc.cvtColor(it, it, Imgproc.COLOR_BGR2RGB) } + + update() + + camera?.close() + currentWebcamIndex = -1 + } + + override fun onResume() { + InputSourceInitializer.runWithTimeout(name, inputSourceManager) { + + camera?.open() + camera?.isOpen == true + } + } + + override fun internalCloneSource(): InputSource = if (isLegacyByIndex) { + CameraSource(webcamIndex, size, rotation) + } else { + CameraSource(webcamName, size, rotation) + } + + + override val fileFilters: FileFilter? get() = null + override val captureTimeNanos: Long get() = capTimeNanos + + + override fun toString(): String { + return "CameraSource($webcamName, $webcamIndex, ${size})" + } + +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSourceAdapter.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSourceAdapter.java deleted file mode 100644 index a3a2fd29..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSourceAdapter.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.github.serivesmejia.eocvsim.input.source; - -import com.github.serivesmejia.eocvsim.input.source.CameraSource; -import com.google.gson.*; - -import java.lang.reflect.Type; - -public class CameraSourceAdapter implements JsonSerializer { - - @Override - public JsonElement serialize(CameraSource src, Type typeOfSrc, JsonSerializationContext context) { - JsonObject obj = new JsonObject(); - - if(src.webcamName != null) { - obj.addProperty("webcamName", src.webcamName); - } else { - obj.addProperty("webcamIndex", src.webcamIndex); - } - - obj.add("size", context.serialize(src.size)); - obj.addProperty("createdOn", src.getCreationTime()); - - return obj; - } - -} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSourceAdapter.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSourceAdapter.kt new file mode 100644 index 00000000..103b4581 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSourceAdapter.kt @@ -0,0 +1,24 @@ +package com.github.serivesmejia.eocvsim.input.source + +import com.google.gson.* +import java.lang.reflect.Type + +class CameraSourceAdapter : JsonSerializer { + + override fun serialize(src: CameraSource, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { + val obj = JsonObject() + + if (src.webcamName.isNotEmpty()) { + obj.addProperty("webcamName", src.webcamName) + } else { + obj.addProperty("webcamIndex", src.webcamIndex) + } + + obj.add("size", context.serialize(src.size)) + obj.addProperty("createdOn", src.creationTime) + + + return obj + } + +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.java deleted file mode 100644 index 8420065b..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (c) 2025 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.input.source; - -import com.github.serivesmejia.eocvsim.input.InputSource; -import com.github.serivesmejia.eocvsim.input.InputSourceInitializer; -import com.google.gson.annotations.Expose; -import io.github.deltacv.visionloop.io.MjpegHttpReader; -import org.opencv.core.Mat; -import org.opencv.core.MatOfByte; -import org.opencv.imgcodecs.Imgcodecs; -import org.opencv.imgproc.Imgproc; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.filechooser.FileFilter; -import java.util.Iterator; - -public class HttpSource extends InputSource { - - @Expose - protected String url; - - private transient MjpegHttpReader mjpegHttpReader; - - private static final Logger logger = LoggerFactory.getLogger(HttpSource.class); - - private transient MatOfByte buf; - private transient Mat img; - - private transient Iterator iterator; - - private transient long capTimeNanos = 0; - - public HttpSource(String url) { - this.url = url; - } - - @Override - public boolean init() { - buf = new MatOfByte(); - img = new Mat(); - - try { - mjpegHttpReader = new MjpegHttpReader(url); - mjpegHttpReader.start(); - } catch (Exception e) { - logger.error("Error while initializing MjpegHttpReader", e); - return false; - } - - try { - iterator = mjpegHttpReader.iterator(); - } catch (Exception e) { - logger.error("Error while getting MjpegHttpReader iterator", e); - return false; - } - - logger.info("HttpSource initialized"); - - return mjpegHttpReader != null && iterator != null; - } - - byte[] frame; - - @Override - public Mat update() { - if (mjpegHttpReader == null) return null; - - boolean result = InputSourceInitializer.INSTANCE.runWithTimeout(name, () -> { - frame = iterator.next(); - return frame != null; - }); - - if(!result) { - return null; - } - - if(!dataIsValidJPEG(frame)) { - logger.error("Received data is not a valid JPEG image"); - return null; - } - - buf.fromArray(frame); - - if(buf.empty()) { - return null; - } - - Mat mat = Imgcodecs.imdecode(buf, Imgcodecs.IMREAD_COLOR); - Imgproc.cvtColor(mat, img, Imgproc.COLOR_BGR2RGBA); - - mat.release(); - - capTimeNanos = System.nanoTime(); - - return img; - } - - @Override - public void reset() { - if (mjpegHttpReader != null) { - mjpegHttpReader.stop(); - mjpegHttpReader = null; - } - } - - @Override - public void close() { - reset(); - } - - @Override - public void onPause() { - if (mjpegHttpReader != null) { - reset(); - } - } - - @Override - public void onResume() { - InputSourceInitializer.INSTANCE.runWithTimeout(name, eocvSim.inputSourceManager, this::init); - } - - @Override - protected InputSource internalCloneSource() { - return new HttpSource(url); - } - - public String getUrl() { - return url; - } - - @Override - public FileFilter getFileFilters() { - return null; - } - - @Override - public long getCaptureTimeNanos() { - return capTimeNanos; - } - - @Override - public String toString() { - return "HttpSource(" + url + ")"; - } - - private static boolean dataIsValidJPEG(byte[] data) { - if (data == null || data.length < 2) { - return false; - } - - int totalBytes = getJPEGSize(data, data.length); - - if (totalBytes == -1) { - return false; - } - - return (data[0] == (byte) 0xFF && - data[1] == (byte) 0xD8 && - data[totalBytes - 2] == (byte) 0xFF && - data[totalBytes - 1] == (byte) 0xD9); - } - - private static int getJPEGSize(byte[] data, int maxLength) { - if (data == null || maxLength < 4) { - return -1; // Invalid or too small to be a JPEG - } - - // Check for SOI marker - if (data[0] != (byte) 0xFF || data[1] != (byte) 0xD8) { - return -1; // Not a JPEG - } - - int pos = 2; // Start after SOI - - while (pos < maxLength - 2) { - // Look for the next marker (0xFF xx) - if (data[pos] == (byte) 0xFF) { - byte marker = data[pos + 1]; - - // End of Image (EOI) found - if (marker == (byte) 0xD9) { - return pos + 2; // JPEG size - } - - // Skip padding bytes (some JPEGs use 0xFF 0x00) - if (marker == (byte) 0x00) { - pos++; - continue; - } - - // Most markers have a 2-byte length field - if ((marker >= (byte) 0xC0 && marker <= (byte) 0xFE) && marker != (byte) 0xD9) { - if (pos + 3 >= maxLength) { - return -1; // Incomplete JPEG - } - - // Read segment length (big-endian) - int segmentLength = ((data[pos + 2] & 0xFF) << 8) | (data[pos + 3] & 0xFF); - - if (segmentLength < 2 || pos + segmentLength >= maxLength) { - return -1; // Corrupt or incomplete JPEG - } - - pos += segmentLength; // Move to next marker - } else { - pos++; // Skip unknown byte - } - } else { - pos++; // Continue searching - } - } - - return -1; // No valid JPEG end found - } -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt new file mode 100644 index 00000000..860a8e5a --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2025 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.input.source + +import com.github.serivesmejia.eocvsim.input.InputSource +import com.github.serivesmejia.eocvsim.input.InputSourceInitializer +import com.github.serivesmejia.eocvsim.input.InputSourceManager + +import com.google.gson.annotations.Expose +import io.github.deltacv.visionloop.io.MjpegHttpReader +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.opencv.core.Mat +import org.opencv.core.MatOfByte +import org.opencv.imgcodecs.Imgcodecs +import org.opencv.imgproc.Imgproc +import org.slf4j.LoggerFactory +import javax.swing.filechooser.FileFilter + +class HttpSource @JvmOverloads constructor( + @Expose @JvmField var url: String = "" +) : InputSource() { + + @delegate:Transient + private val inputSourceManager: InputSourceManager by inject() + + @Transient private var mjpegHttpReader: MjpegHttpReader? = null + + @Transient private var buf: MatOfByte? = null + @Transient private var img: Mat? = null + + @Transient private var iterator: Iterator? = null + + @Transient private var capTimeNanos: Long = 0 + + @Transient private val logger = LoggerFactory.getLogger(javaClass) + + override fun init(): Boolean { + buf = MatOfByte() + img = Mat() + + try { + mjpegHttpReader = MjpegHttpReader(url) + mjpegHttpReader!!.start() + } catch (e: Exception) { + logger.error("Error while initializing MjpegHttpReader", e) + return false + } + + try { + iterator = mjpegHttpReader!!.iterator() + } catch (e: Exception) { + logger.error("Error while getting MjpegHttpReader iterator", e) + return false + } + + logger.info("HttpSource initialized") + + return mjpegHttpReader != null && iterator != null + } + + @Transient private var frame: ByteArray? = null + + override fun update(): Mat? { + if (mjpegHttpReader == null || iterator == null) return null + + val result = InputSourceInitializer.runWithTimeout(name) { + + frame = iterator!!.next() + frame != null + } + + if (!result || frame == null) { + return null + } + + val frameData = frame!! + + if (!dataIsValidJPEG(frameData)) { + logger.error("Received data is not a valid JPEG image") + return null + } + + buf!!.fromArray(*frameData) + + if (buf!!.empty()) { + return null + } + + val mat = Imgcodecs.imdecode(buf, Imgcodecs.IMREAD_COLOR) + Imgproc.cvtColor(mat, img, Imgproc.COLOR_BGR2RGBA) + + mat.release() + + capTimeNanos = System.nanoTime() + + return img + } + + override fun reset() { + mjpegHttpReader?.stop() + mjpegHttpReader = null + } + + override fun close() { + reset() + } + + override fun onPause() { + if (mjpegHttpReader != null) { + reset() + } + } + + override fun onResume() { + InputSourceInitializer.runWithTimeout(name, inputSourceManager) { + + + init() + } + } + + override fun internalCloneSource(): InputSource = HttpSource(url) + + override val fileFilters: FileFilter? get() = null + override val captureTimeNanos: Long get() = capTimeNanos + + + override fun toString(): String { + return "HttpSource($url)" + } + + companion object { + private fun dataIsValidJPEG(data: ByteArray?): Boolean { + if (data == null || data.size < 2) { + return false + } + + val totalBytes = getJPEGSize(data, data.size) + + if (totalBytes == -1) { + return false + } + + return (data[0] == 0xFF.toByte() && + data[1] == 0xD8.toByte() && + data[totalBytes - 2] == 0xFF.toByte() && + data[totalBytes - 1] == 0xD9.toByte()) + } + + private fun getJPEGSize(data: ByteArray?, maxLength: Int): Int { + if (data == null || maxLength < 4) { + return -1 // Invalid or too small to be a JPEG + } + + // Check for SOI marker + if (data[0] != 0xFF.toByte() || data[1] != 0xD8.toByte()) { + return -1 // Not a JPEG + } + + var pos = 2 // Start after SOI + + while (pos < maxLength - 2) { + // Look for the next marker (0xFF xx) + if (data[pos] == 0xFF.toByte()) { + val marker = data[pos + 1] + + // End of Image (EOI) found + if (marker == 0xD9.toByte()) { + return pos + 2 // JPEG size + } + + // Skip padding bytes (some JPEGs use 0xFF 0x00) + if (marker == 0x00.toByte()) { + pos++ + continue + } + + // Most markers have a 2-byte length field + if ((marker >= 0xC0.toByte() && marker <= 0xFE.toByte()) && marker != 0xD9.toByte()) { + if (pos + 3 >= maxLength) { + return -1 // Incomplete JPEG + } + + // Read segment length (big-endian) + val segmentLength = ((data[pos + 2].toInt() and 0xFF) shl 8) or (data[pos + 3].toInt() and 0xFF) + + if (segmentLength < 2 || pos + segmentLength >= maxLength) { + return -1 // Corrupt or incomplete JPEG + } + + pos += segmentLength // Move to next marker + } else { + pos++ // Skip unknown byte + } + } else { + pos++ // Continue searching + } + } + + return -1 // No valid JPEG end found + } + } +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.java deleted file mode 100644 index 42c48f14..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.input.source; - -import com.github.serivesmejia.eocvsim.input.InputSource; -import com.github.serivesmejia.eocvsim.util.FileFilters; -import com.google.gson.annotations.Expose; -import org.opencv.core.Mat; -import org.opencv.core.Size; -import org.opencv.imgcodecs.Imgcodecs; -import org.opencv.imgproc.Imgproc; -import org.openftc.easyopencv.MatRecycler; - -import javax.swing.filechooser.FileFilter; - -public class ImageSource extends InputSource { - - @Expose - private final String imgPath; - @Expose - private volatile Size size; - - private volatile transient MatRecycler.RecyclableMat img; - private volatile transient MatRecycler.RecyclableMat lastCloneTo; - - private volatile transient boolean initialized = false; - - private volatile transient MatRecycler matRecycler = new MatRecycler(2); - - public ImageSource(String imgPath) { - this(imgPath, null); - } - - public ImageSource(String imgPath, Size size) { - this.imgPath = imgPath; - this.size = size; - } - - @Override - public boolean init() { - - if (initialized) return false; - initialized = true; - - if (matRecycler == null) matRecycler = new MatRecycler(2); - - readImage(); - - return img != null && !img.empty(); - - } - - @Override - public void onPause() { - //if(img != null) img.release(); - } - - @Override - public void onResume() { - } - - @Override - public void reset() { - - if (!initialized) return; - - if (lastCloneTo != null) { - lastCloneTo.returnMat(); - lastCloneTo = null; - } - - if (img != null) { - img.returnMat(); - img = null; - } - - matRecycler.releaseAll(); - - initialized = false; - - } - - public void close() { - - if (img != null) { - matRecycler.returnMat(img); - img = null; - } - - if (lastCloneTo != null) { - lastCloneTo.returnMat(); - lastCloneTo = null; - } - - matRecycler.releaseAll(); - - } - - public void readImage() { - - Mat readMat = Imgcodecs.imread(this.imgPath); - - if (img == null) img = matRecycler.takeMatOrNull(); - - if (readMat.empty()) { - return; - } - - readMat.copyTo(img); - readMat.release(); - - if (this.size != null) { - Imgproc.resize(img, img, this.size, 0.0, 0.0, Imgproc.INTER_AREA); - } else { - this.size = img.size(); - } - - Imgproc.cvtColor(img, img, Imgproc.COLOR_BGR2RGB); - - } - - @Override - public Mat update() { - if (lastCloneTo == null) lastCloneTo = matRecycler.takeMatOrNull(); - - if (img == null) return null; - - lastCloneTo.release(); - img.copyTo(lastCloneTo); - - return lastCloneTo; - } - - public String getImgPath() { - return imgPath; - } - - @Override - public void cleanIfDirty() { - readImage(); - } - - @Override - protected InputSource internalCloneSource() { - return new ImageSource(imgPath, size); - } - - @Override - public void setSize(Size size) { - this.size = size; - } - - @Override - public Size getSize() { - return size; - } - - @Override - public FileFilter getFileFilters() { - return FileFilters.imagesFilter; - } - - @Override - public long getCaptureTimeNanos() { - return System.nanoTime(); - } - - @Override - public String toString() { - if (size == null) size = new Size(); - return "ImageSource(\"" + imgPath + "\", " + (size != null ? size.toString() : "null") + ")"; - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.kt new file mode 100644 index 00000000..3fdae0b7 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.kt @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2021 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.input.source + +import com.github.serivesmejia.eocvsim.input.InputSource +import com.github.serivesmejia.eocvsim.util.FileFilters +import com.google.gson.annotations.Expose +import org.opencv.core.Mat +import org.opencv.core.Size +import org.opencv.imgcodecs.Imgcodecs +import org.opencv.imgproc.Imgproc +import org.openftc.easyopencv.MatRecycler +import javax.swing.filechooser.FileFilter + +class ImageSource @JvmOverloads constructor( + @Expose @JvmField var imgPath: String = "", + @Expose @JvmField var size: Size = Size() + +) : InputSource() { + + @Transient private var img: MatRecycler.RecyclableMat? = null + @Transient private var lastCloneTo: MatRecycler.RecyclableMat? = null + + @Transient private var initialized = false + + @Transient private var matRecycler = MatRecycler(2) + + override fun init(): Boolean { + if (initialized) return false + initialized = true + + readImage() + + return img != null && !img!!.empty() + } + + override fun onPause() {} + + override fun onResume() {} + + override fun reset() { + if (!initialized) return + + lastCloneTo?.returnMat() + lastCloneTo = null + + img?.returnMat() + img = null + + matRecycler.releaseAll() + + initialized = false + } + + override fun close() { + img?.let { + matRecycler.returnMat(it) + img = null + } + + lastCloneTo?.let { + it.returnMat() + lastCloneTo = null + } + + matRecycler.releaseAll() + } + + fun readImage() { + val readMat = Imgcodecs.imread(imgPath) + + if (img == null) img = matRecycler.takeMatOrNull() + + if (readMat.empty()) { + return + } + + readMat.copyTo(img) + readMat.release() + + if (this.size.area() != 0.0) { + Imgproc.resize(img, img, this.size, 0.0, 0.0, Imgproc.INTER_AREA) + } else { + this.size = img!!.size() + } + + + Imgproc.cvtColor(img, img, Imgproc.COLOR_BGR2RGB) + } + + override fun update(): Mat? { + if (lastCloneTo == null) lastCloneTo = matRecycler.takeMatOrNull() + + if (img == null || lastCloneTo == null) return null + + lastCloneTo!!.release() + img!!.copyTo(lastCloneTo) + + return lastCloneTo + } + + override fun cleanIfDirty() { + readImage() + } + + override fun internalCloneSource() = ImageSource(imgPath, size) + + override fun setSize(size: Size) { + this.size = size + } + + override fun getSize() = size + + + override val fileFilters: FileFilter get() = FileFilters.imagesFilter + override val captureTimeNanos: Long get() = System.nanoTime() + + + override fun toString(): String { + return "ImageSource(\"$imgPath\", $size)" + + } + +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/NullSource.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/NullSource.kt similarity index 54% rename from EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/NullSource.java rename to EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/NullSource.kt index 76380b88..8840528a 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/NullSource.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/NullSource.kt @@ -1,78 +1,52 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.input.source; - -import com.github.serivesmejia.eocvsim.input.InputSource; -import org.opencv.core.Mat; - -import javax.swing.filechooser.FileFilter; - -public class NullSource extends InputSource { - @Override - public boolean init() { - return true; - } - - @Override - public void reset() { - - } - - @Override - public void close() { - - } - - Mat emptyMat = new Mat(); - - @Override - public Mat update() { - return emptyMat; - } - - @Override - public void onPause() { - - } - - @Override - public void onResume() { - - } - - @Override - protected InputSource internalCloneSource() { - return new NullSource(); - } - - @Override - public FileFilter getFileFilters() { - return null; - } - - @Override - public long getCaptureTimeNanos() { - return System.nanoTime(); - } -} +/* + * Copyright (c) 2026 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.input.source + +import com.github.serivesmejia.eocvsim.input.InputSource +import org.opencv.core.Mat +import javax.swing.filechooser.FileFilter + +class NullSource : InputSource() { + + override fun init(): Boolean = true + + override fun reset() {} + + override fun close() {} + + @Transient private val emptyMat = Mat() + + override fun update(): Mat = emptyMat + + override fun onPause() {} + + override fun onResume() {} + + override fun internalCloneSource(): InputSource = NullSource() + + override val fileFilters: FileFilter? = null + override val captureTimeNanos: Long get() = System.nanoTime() + + +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.java deleted file mode 100644 index a02d7617..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.input.source; - -import com.github.serivesmejia.eocvsim.input.InputSource; -import com.github.serivesmejia.eocvsim.util.FileFilters; -import com.github.serivesmejia.eocvsim.util.fps.FpsLimiter; -import com.google.gson.annotations.Expose; -import org.opencv.core.Mat; -import org.opencv.core.Size; -import org.opencv.imgproc.Imgproc; -import org.opencv.videoio.VideoCapture; -import org.opencv.videoio.Videoio; -import org.openftc.easyopencv.MatRecycler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.swing.filechooser.FileFilter; - -public class VideoSource extends InputSource { - - @Expose - private String videoPath = null; - - private transient VideoCapture video; - - private transient FpsLimiter fpsLimiter = new FpsLimiter(30); - - private transient MatRecycler.RecyclableMat lastFramePaused; - private transient MatRecycler.RecyclableMat lastFrame; - - private transient boolean initialized; - - @Expose - private volatile Size size; - - private volatile transient MatRecycler matRecycler = null; - - private transient double lastFramePosition = 0; - - private transient long capTimeNanos = 0; - - private transient Logger logger = LoggerFactory.getLogger(getClass()); - - public VideoSource() { - } - - public VideoSource(String videoPath, Size size) { - this.videoPath = videoPath; - this.size = size; - } - - @Override - public boolean init() { - if (initialized) return false; - initialized = true; - - video = new VideoCapture(); - video.open(videoPath); - - if (!video.isOpened()) { - logger.error("Unable to open video " + videoPath); - return false; - } - - if (matRecycler == null) matRecycler = new MatRecycler(4); - - MatRecycler.RecyclableMat newFrame = matRecycler.takeMatOrNull(); - newFrame.release(); - - video.read(newFrame); - - if (newFrame.empty()) { - logger.error("Unable to open video " + videoPath + ", returned Mat was empty."); - return false; - } - - fpsLimiter.setMaxFPS(video.get(Videoio.CAP_PROP_FPS)); - - newFrame.release(); - matRecycler.returnMat(newFrame); - - return true; - } - - @Override - public void reset() { - if (!initialized) return; - - if (video != null && video.isOpened()) video.release(); - - if (lastFrame != null && lastFrame.isCheckedOut()) - lastFrame.returnMat(); - if (lastFramePaused != null && lastFramePaused.isCheckedOut()) - lastFramePaused.returnMat(); - - matRecycler.releaseAll(); - - video = null; - initialized = false; - } - - @Override - public void close() { - if (video != null && video.isOpened()) video.release(); - if (lastFrame != null && lastFrame.isCheckedOut()) lastFrame.returnMat(); - - if (lastFramePaused != null) { - lastFramePaused.returnMat(); - lastFramePaused = null; - } - } - - @Override - public Mat update() { - if (isPaused) { - return lastFramePaused; - } else if (lastFramePaused != null) { - lastFramePaused.returnMat(); - lastFramePaused = null; - } - - try { - fpsLimiter.sync(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - - if (lastFrame == null) lastFrame = matRecycler.takeMatOrNull(); - if (video == null) return lastFrame; - - MatRecycler.RecyclableMat newFrame = matRecycler.takeMatOrNull(); - - video.read(newFrame); - capTimeNanos = System.nanoTime(); - - //with videocapture for video files, when an empty mat is returned - //the most likely reason is that the video ended, so we set the - //playback position back to 0 for looping in here and start over - //in next update - if (newFrame.empty()) { - newFrame.returnMat(); - - this.reset(); - this.init(); - return lastFrame; - } - - if (size == null) size = lastFrame.size(); - - Imgproc.cvtColor(newFrame, lastFrame, Imgproc.COLOR_BGR2RGB); - Imgproc.resize(lastFrame, lastFrame, size, 0.0, 0.0, Imgproc.INTER_AREA); - - matRecycler.returnMat(newFrame); - - return lastFrame; - } - - @Override - public void onPause() { - if (lastFrame != null) lastFrame.release(); - if (lastFramePaused == null) lastFramePaused = matRecycler.takeMatOrNull(); - - video.read(lastFramePaused); - - Imgproc.cvtColor(lastFramePaused, lastFramePaused, Imgproc.COLOR_BGR2RGB); - Imgproc.resize(lastFramePaused, lastFramePaused, size, 0.0, 0.0, Imgproc.INTER_AREA); - - update(); - - lastFramePosition = video.get(Videoio.CAP_PROP_POS_FRAMES); - - video.release(); - video = null; - } - - @Override - public void onResume() { - video = new VideoCapture(); - video.open(videoPath); - video.set(Videoio.CAP_PROP_POS_FRAMES, lastFramePosition); - } - - public String getVideoPath() { - return videoPath; - } - - @Override - protected InputSource internalCloneSource() { - return new VideoSource(videoPath, size); - } - - @Override - public Size getSize() { return size; } - @Override - public void setSize(Size size) { - this.size = size; - } - - @Override - public FileFilter getFileFilters() { - return FileFilters.videoMediaFilter; - } - - @Override - public long getCaptureTimeNanos() { - return capTimeNanos; - } - - @Override - public String toString() { - return "VideoSource(" + videoPath + ", " + (size != null ? size.toString() : "null") + ")"; - } - -} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt new file mode 100644 index 00000000..bdf2f27f --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2021 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.input.source + +import com.github.serivesmejia.eocvsim.input.InputSource +import com.github.serivesmejia.eocvsim.util.FileFilters +import com.github.serivesmejia.eocvsim.util.fps.FpsLimiter +import com.google.gson.annotations.Expose +import org.opencv.core.Mat +import org.opencv.core.Size +import org.opencv.imgproc.Imgproc +import org.opencv.videoio.VideoCapture +import org.opencv.videoio.Videoio +import org.openftc.easyopencv.MatRecycler +import org.slf4j.LoggerFactory +import javax.swing.filechooser.FileFilter + +class VideoSource @JvmOverloads constructor( + @Expose @JvmField var videoPath: String = "", + @Expose @JvmField var size: Size = Size() + +) : InputSource() { + + @Transient private var video: VideoCapture? = null + + @Transient private val fpsLimiter = FpsLimiter(30.0) + + @Transient private var lastFramePaused: MatRecycler.RecyclableMat? = null + @Transient private var lastFrame: MatRecycler.RecyclableMat? = null + + @Transient private var initialized = false + + @Transient private var matRecycler: MatRecycler? = null + + @Transient private var lastFramePosition = 0.0 + + @Transient private var capTimeNanos: Long = 0 + + @Transient private val logger = LoggerFactory.getLogger(javaClass) + + override fun init(): Boolean { + if (initialized) return false + initialized = true + + video = VideoCapture() + video!!.open(videoPath) + + if (!video!!.isOpened) { + logger.error("Unable to open video $videoPath") + return false + } + + if (matRecycler == null) matRecycler = MatRecycler(4) + + val newFrame = matRecycler!!.takeMatOrNull() + newFrame!!.release() + + video!!.read(newFrame) + + if (newFrame.empty()) { + logger.error("Unable to open video $videoPath, returned Mat was empty.") + return false + } + + fpsLimiter.maxFPS = video!!.get(Videoio.CAP_PROP_FPS) + + newFrame.release() + matRecycler!!.returnMat(newFrame) + + return true + } + + override fun reset() { + if (!initialized) return + + if (video?.isOpened == true) video!!.release() + + if (lastFrame?.isCheckedOut == true) lastFrame!!.returnMat() + if (lastFramePaused?.isCheckedOut == true) lastFramePaused!!.returnMat() + + matRecycler?.releaseAll() + + video = null + initialized = false + } + + override fun close() { + if (video?.isOpened == true) video!!.release() + if (lastFrame?.isCheckedOut == true) lastFrame!!.returnMat() + + lastFramePaused?.let { + it.returnMat() + lastFramePaused = null + } + } + + override fun update(): Mat? { + if (isPaused) { + return lastFramePaused + } else if (lastFramePaused != null) { + lastFramePaused!!.returnMat() + lastFramePaused = null + } + + try { + fpsLimiter.sync() + } catch (_: InterruptedException) { + Thread.currentThread().interrupt() + } + + if (lastFrame == null) lastFrame = matRecycler?.takeMatOrNull() + if (video == null) return lastFrame + + val newFrame = matRecycler?.takeMatOrNull() ?: return lastFrame + + video!!.read(newFrame) + capTimeNanos = System.nanoTime() + + if (newFrame.empty()) { + newFrame.returnMat() + + reset() + init() + return lastFrame + } + + if (size.area() == 0.0) size = lastFrame!!.size() + + Imgproc.cvtColor(newFrame, lastFrame, Imgproc.COLOR_BGR2RGB) + Imgproc.resize(lastFrame, lastFrame, size, 0.0, 0.0, Imgproc.INTER_AREA) + + matRecycler?.returnMat(newFrame) + + return lastFrame + } + + override fun onPause() { + lastFrame?.release() + + if (lastFramePaused == null) lastFramePaused = matRecycler?.takeMatOrNull() + + video?.read(lastFramePaused) + + lastFramePaused?.let { + Imgproc.cvtColor(it, it, Imgproc.COLOR_BGR2RGB) + Imgproc.resize(it, it, size, 0.0, 0.0, Imgproc.INTER_AREA) + } + + update() + + lastFramePosition = video?.get(Videoio.CAP_PROP_POS_FRAMES) ?: 0.0 + + video?.release() + video = null + } + + override fun onResume() { + video = VideoCapture() + video!!.open(videoPath) + video!!.set(Videoio.CAP_PROP_POS_FRAMES, lastFramePosition) + } + + override fun internalCloneSource(): InputSource = VideoSource(videoPath, size) + + override fun getSize(): Size = size + + + override fun setSize(size: Size) { + this.size = size + } + + override val fileFilters: FileFilter get() = FileFilters.videoMediaFilter + override val captureTimeNanos: Long get() = capTimeNanos + + + override fun toString(): String { + return "VideoSource($videoPath, $size)" + + } + +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/RecordingManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/RecordingManager.kt new file mode 100644 index 00000000..565777a1 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/RecordingManager.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.output + +import com.github.serivesmejia.eocvsim.config.ConfigManager +import com.github.serivesmejia.eocvsim.gui.DialogFactory +import com.github.serivesmejia.eocvsim.gui.Visualizer +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import com.github.serivesmejia.eocvsim.util.FileFilters +import com.github.serivesmejia.eocvsim.util.SysUtil +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import io.github.deltacv.common.util.loggerForThis +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named +import java.io.File +import javax.swing.filechooser.FileFilter + +class RecordingManager : KoinComponent { + + val configManager: ConfigManager by inject() + val pipelineManager: PipelineManager by inject() + val visualizer: Visualizer by inject() + val dialogFactory: DialogFactory by inject() + val onMainUpdate: EventHandler by inject(named("onMainLoop")) + + private val logger by loggerForThis() + + var currentRecordingSession: VideoRecordingSession? = null + private set + + fun isCurrentlyRecording() = currentRecordingSession != null + + fun startRecordingSession() { + if (currentRecordingSession == null) { + currentRecordingSession = VideoRecordingSession( + configManager.config.videoRecordingFps.fps.toDouble(), configManager.config.videoRecordingSize + ) + + currentRecordingSession!!.startRecordingSession() + + logger.info("Recording session started") + + pipelineManager.pipelineOutputPosters.add(currentRecordingSession!!.matPoster) + } + } + + fun stopRecordingSession() { + currentRecordingSession?.let { itVideo -> + visualizer.pipelineSelectorPanel.buttonsPanel.pipelineRecordBtt.isEnabled = false + + itVideo.stopRecordingSession() + pipelineManager.pipelineOutputPosters.remove(itVideo.matPoster) + + logger.info("Recording session stopped") + + dialogFactory.createFileChooser( + visualizer.frame, DialogFactory.FileChooser.Mode.SAVE_FILE_SELECT, "", FileFilters.recordedVideoFilter + ).addCloseListener { _: Int, file: File?, _: FileFilter? -> + onMainUpdate.once { + if (file != null) { + var correctedFile = file + val extension = SysUtil.getExtensionByStringHandling(file.name) + if (!extension.isPresent || extension.get() != "avi") { + correctedFile = File(file.absolutePath + ".avi") + } + + + itVideo.saveTo(correctedFile) + } + + visualizer.pipelineSelectorPanel.buttonsPanel.pipelineRecordBtt.isEnabled = true + currentRecordingSession = null + } + } + } + } + +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt index 932b2e35..97ded9bc 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt @@ -23,8 +23,10 @@ package com.github.serivesmejia.eocvsim.pipeline -import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.gui.DialogFactory +import com.github.serivesmejia.eocvsim.gui.Visualizer +import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.github.serivesmejia.eocvsim.pipeline.compiler.CompiledPipelineManager import com.github.serivesmejia.eocvsim.pipeline.handler.PipelineHandler import com.github.serivesmejia.eocvsim.pipeline.instantiator.DefaultPipelineInstantiator @@ -33,11 +35,13 @@ import com.github.serivesmejia.eocvsim.pipeline.instantiator.processor.Processor import com.github.serivesmejia.eocvsim.pipeline.util.PipelineExceptionTracker import com.github.serivesmejia.eocvsim.pipeline.util.PipelineSnapshot import com.github.serivesmejia.eocvsim.tuner.TunableFieldRegistry +import com.github.serivesmejia.eocvsim.util.ClasspathScan import com.github.serivesmejia.eocvsim.util.ReflectUtil import com.github.serivesmejia.eocvsim.util.StrUtil import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.util.fps.FpsCounter +import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager import io.github.deltacv.common.util.loggerForThis import io.github.deltacv.common.image.MatPoster import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator @@ -48,6 +52,10 @@ import kotlinx.coroutines.* import org.firstinspires.ftc.robotcore.external.Telemetry import org.firstinspires.ftc.robotcore.internal.opmode.EOCVSimTelemetryImpl import org.firstinspires.ftc.vision.VisionProcessor +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named +import com.github.serivesmejia.eocvsim.EOCVSim import org.opencv.core.Mat import org.openftc.easyopencv.OpenCvPipeline import org.openftc.easyopencv.OpenCvViewport @@ -57,13 +65,22 @@ import kotlin.coroutines.EmptyCoroutineContext import kotlin.math.roundToLong @OptIn(DelicateCoroutinesApi::class) -class PipelineManager( - var eocvSim: EOCVSim, - val pipelineStatisticsCalculator: PipelineStatisticsCalculator -) { +class PipelineManager : KoinComponent { - companion object { + val onMainUpdate: EventHandler by inject(named("onMainLoop")) + val params: EOCVSim.Parameters by inject() + + val dialogFactory: DialogFactory by inject() + val configManager: ConfigManager by inject() + val inputSourceManager: InputSourceManager by inject() + val pipelineStatisticsCalculator: PipelineStatisticsCalculator by inject() + val visualizer: Visualizer by inject() + val classpathScan: ClasspathScan by inject() + val scope: CoroutineScope by inject() + val compiledPipelineManager: CompiledPipelineManager by inject() + + companion object { var staticSnapshot: PipelineSnapshot? = null private set } @@ -160,10 +177,6 @@ class PipelineManager( TunableFieldRegistry.hasTunableFieldFor(it.type) } - //manages and builds pipelines in runtime - @JvmField - val compiledPipelineManager = CompiledPipelineManager(this) - private val pipelineHandlers = mutableListOf() private val pipelineInstantiators = mutableMapOf, PipelineInstantiator>() @@ -184,10 +197,10 @@ class PipelineManager( compiledPipelineManager.init() - eocvSim.classpathScan.join() + classpathScan.join() //scan for pipelines - for (pipelineClass in eocvSim.classpathScan.scanResult.pipelineClasses) { + for (pipelineClass in classpathScan.scanResult!!.pipelineClasses) { addPipelineClass(pipelineClass) } @@ -212,7 +225,7 @@ class PipelineManager( val telemetry = currentTelemetry if (openedPipelineOutputCount <= 3) { - DialogFactory.createPipelineOutput(eocvSim) + dialogFactory.createPipelineOutput() openedPipelineOutputCount++ } @@ -236,7 +249,7 @@ class PipelineManager( onUpdate { if (currentPipeline != null) { for (pipelineHandler in pipelineHandlers) { - pipelineHandler.processFrame(eocvSim.inputSourceManager.currentInputSource) + pipelineHandler.processFrame(inputSourceManager.currentInputSource) } } } @@ -263,7 +276,7 @@ class PipelineManager( private fun applyStaticSnapOrDef() { onUpdate.once { if (!applyStaticSnapshot()) { - val params = eocvSim.params + val params = this.params // changing to the initial pipeline, defined by the eocv sim parameters or the default pipeline if (params.initialPipelineName != null) { @@ -323,7 +336,7 @@ class PipelineManager( pipelineStatisticsCalculator.newPipelineFrameStart() //run our pipeline in the background until it finishes or gets cancelled - val pipelineJob = eocvSim.scope.launch(currentPipelineContext!!) { + val pipelineJob = scope.launch(currentPipelineContext!!) { try { //if we have a pipeline, we run it right here, passing the input mat //given to us. we'll post the frame the pipeline returns as long @@ -406,7 +419,7 @@ class PipelineManager( } runBlocking { - val configTimeout = eocvSim.config.pipelineTimeout + val configTimeout = configManager.config.pipelineTimeout //allow double timeout if we haven't initialized the pipeline val timeout = if (hasInitCurrentPipeline) { @@ -449,11 +462,11 @@ class PipelineManager( //similar to pipeline processFrame, call the user function in the background //and wait for some X timeout for the user to finisih doing what it has to do. - val viewportTappedJob = eocvSim.scope.launch(currentPipelineContext ?: EmptyCoroutineContext) { + val viewportTappedJob = scope.launch(currentPipelineContext ?: EmptyCoroutineContext) { pipeline.onViewportTapped() } - val configTimeoutMs = eocvSim.config.pipelineTimeout.ms + val configTimeoutMs = configManager.config.pipelineTimeout.ms try { //perform the timeout here (we'll block for a bit @@ -633,8 +646,8 @@ class PipelineManager( setPaused(false) - if (eocvSim.configManager.config.pauseOnImages && pauseOnImages) { - eocvSim.inputSourceManager.pauseIfImageTwoFrames() + if (configManager.config.pauseOnImages && pauseOnImages) { + inputSourceManager.pauseIfImageTwoFrames() } onPipelineChange.run() @@ -686,14 +699,14 @@ class PipelineManager( currentPipelineIndex = index currentPipelineData = data - forceChangePipeline( - clazz = data.clazz, - applyLatestSnapshot = applyLatestSnapshot, - applyStaticSnapshot = applyStaticSnapshot - ) - - eocvSim.visualizer.pipelineSelectorPanel.selectedIndex = index - } + forceChangePipeline( + clazz = data.clazz, + applyLatestSnapshot = applyLatestSnapshot, + applyStaticSnapshot = applyStaticSnapshot + ) + + visualizer.pipelineSelectorPanel.selectedIndex = index + } /** * Change to the requested pipeline only if we're @@ -798,7 +811,8 @@ class PipelineManager( @JvmOverloads fun requestSetPaused(paused: Boolean, pauseReason: PauseReason = PauseReason.USER_REQUESTED) { - eocvSim.onMainUpdate.once { setPaused(paused, pauseReason) } + onMainUpdate.once { setPaused(paused, pauseReason) } + } fun reloadPipelineByName() { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt index 2e2fc218..efb4879c 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt @@ -38,7 +38,21 @@ import com.qualcomm.robotcore.util.ElapsedTime import kotlinx.coroutines.* import java.io.File -class CompiledPipelineManager(private val pipelineManager: PipelineManager) { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.gui.Visualizer +import com.github.serivesmejia.eocvsim.util.ClasspathScan +import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager +import org.koin.core.qualifier.named + +class CompiledPipelineManager : KoinComponent { + + private val pipelineManager: PipelineManager by inject() + private val visualizer: Visualizer by inject() + private val dialogFactory: DialogFactory by inject() + private val scope: CoroutineScope by inject() + private val onMainLoop: EventHandler by inject(named("onMainLoop")) companion object { val logger by loggerForThis() @@ -84,23 +98,22 @@ class CompiledPipelineManager(private val pipelineManager: PipelineManager) { var isBuildRunning = false private set - val workspaceManager get() = pipelineManager.eocvSim.workspaceManager - val eocvSim get() = pipelineManager.eocvSim + val workspaceManager: WorkspaceManager by inject() fun init() { logger.info("Initializing...") onBuildStart { - eocvSim.onMainUpdate.once { - eocvSim.visualizer.menuBar.workspCompile.isEnabled = false - eocvSim.visualizer.pipelineSelectorPanel.buttonsPanel.pipelineCompileBtt.isEnabled = false + onMainLoop.once { + visualizer.menuBar.workspCompile.isEnabled = false + visualizer.pipelineSelectorPanel.buttonsPanel.pipelineCompileBtt.isEnabled = false } } onBuildEnd { - eocvSim.onMainUpdate.once { - eocvSim.visualizer.menuBar.workspCompile.isEnabled = true - eocvSim.visualizer.pipelineSelectorPanel.buttonsPanel.pipelineCompileBtt.isEnabled = true + onMainLoop.once { + visualizer.menuBar.workspCompile.isEnabled = true + visualizer.pipelineSelectorPanel.buttonsPanel.pipelineCompileBtt.isEnabled = true } } @@ -167,8 +180,7 @@ class CompiledPipelineManager(private val pipelineManager: PipelineManager) { PipelineCompileStatus.NO_SOURCE -> { //delete jar if we had no sources, the most logical outcome in this case deleteJarFile() - if(pipelineManager.eocvSim.visualizer.hasFinishedInitializing) - pipelineManager.onPipelineListRefresh.run() + pipelineManager.onPipelineListRefresh.run() "Build cancelled, no source files to compile $messageEnd" } @@ -189,13 +201,15 @@ class CompiledPipelineManager(private val pipelineManager: PipelineManager) { logger.warn("$lastBuildOutputMessage\n") if(result.status == PipelineCompileStatus.FAILED && !Output.isAlreadyOpened) - DialogFactory.createBuildOutput(pipelineManager.eocvSim) + withContext(Dispatchers.Main) { + dialogFactory.createBuildOutput() + } } onBuildEnd.callRightAway = true onBuildEnd.run() - eocvSim.scope.launch { + scope.launch { delay(1000) onBuildEnd.callRightAway = false } @@ -225,7 +239,7 @@ class CompiledPipelineManager(private val pipelineManager: PipelineManager) { lastBuildResult = PipelineCompileResult(PipelineCompileStatus.FAILED, lastBuildOutputMessage!!) if(!Output.isAlreadyOpened) - DialogFactory.createBuildOutput(pipelineManager.eocvSim) + dialogFactory.createBuildOutput() lastBuildResult!! } @@ -234,12 +248,12 @@ class CompiledPipelineManager(private val pipelineManager: PipelineManager) { @OptIn(DelicateCoroutinesApi::class) fun asyncBuild( endCallback: (PipelineCompileResult) -> Unit = {} - ) = eocvSim.scope.launch(Dispatchers.IO) { + ) = scope.launch(Dispatchers.IO) { if(PipelineCompiler.IS_USABLE) { endCallback(build()) } else { - eocvSim.onMainUpdate.once { - eocvSim.visualizer.compilerUnsupported() + onMainLoop.once { + visualizer.compilerUnsupported() } } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/DialogFactoryApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/DialogFactoryApiImpl.kt index b59f1f30..ae1db405 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/DialogFactoryApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/DialogFactoryApiImpl.kt @@ -32,16 +32,21 @@ import io.github.deltacv.eocvsim.plugin.api.DialogFactoryApi import io.github.deltacv.eocvsim.plugin.api.HookApi import io.github.deltacv.eocvsim.plugin.api.InputSourceApi import io.github.deltacv.eocvsim.plugin.api.JFileChooserApi +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import java.io.File import javax.swing.filechooser.FileFilter -class DialogFactoryApiImpl(owner: EOCVSimPlugin, val internalVisualizer: Visualizer) : DialogFactoryApi(owner) { +class DialogFactoryApiImpl(owner: EOCVSimPlugin, val internalVisualizer: Visualizer) : DialogFactoryApi(owner), KoinComponent { + + val dialogFactory: DialogFactory by inject() + override fun createYesOrNo( message: String, subMessage: String, result: (Boolean) -> Unit ) = apiImpl { - DialogFactory.createYesOrNo(internalVisualizer.frame, message, subMessage) { + dialogFactory.createYesOrNo(internalVisualizer.frame, message, subMessage) { result(it == 0) } } @@ -57,7 +62,7 @@ class DialogFactoryApiImpl(owner: EOCVSimPlugin, val internalVisualizer: Visuali JFileChooserApi.Mode.SAVE_FILE -> DialogFactory.FileChooser.Mode.SAVE_FILE_SELECT } - val fileChooser = DialogFactory.createFileChooser( + val fileChooser = dialogFactory.createFileChooser( internalVisualizer.frame, internalMode, initialFileName, @@ -78,31 +83,31 @@ class DialogFactoryApiImpl(owner: EOCVSimPlugin, val internalVisualizer: Visuali InputSourceApi.Type.HTTP -> SourceType.HTTP } - DialogFactory.createSourceDialog(internalVisualizer.eocvSim, type, initialFile) + dialogFactory.createSourceDialog(type, initialFile) } override fun createSourceDialog() = apiImpl { - DialogFactory.createSourceExDialog(internalVisualizer.eocvSim) + dialogFactory.createSourceExDialog() } override fun createConfigDialog() = apiImpl { - DialogFactory.createConfigDialog(internalVisualizer.eocvSim) + dialogFactory.createConfigDialog() } override fun createAboutDialog() = apiImpl { - DialogFactory.createAboutDialog(internalVisualizer.eocvSim) + dialogFactory.createAboutDialog() } override fun createOutputDialog(wasManuallyOpened: Boolean) = apiImpl { - DialogFactory.createOutput(internalVisualizer.eocvSim, wasManuallyOpened) + dialogFactory.createOutput(wasManuallyOpened) } override fun createBuildOutputDialog() = apiImpl { - DialogFactory.createBuildOutput(internalVisualizer.eocvSim) + dialogFactory.createBuildOutput() } override fun createPipelineOutputDialog() = apiImpl { - DialogFactory.createPipelineOutput(internalVisualizer.eocvSim) + dialogFactory.createPipelineOutput() } override fun createSplashScreen(closeHook: HookApi) = apiImpl { @@ -111,23 +116,23 @@ class DialogFactoryApiImpl(owner: EOCVSimPlugin, val internalVisualizer: Visuali internalCloseHandler.run() } - DialogFactory.createSplashScreen(internalCloseHandler) + dialogFactory.createSplashScreen(internalCloseHandler) } override fun createIAmADialog() = apiImpl { - DialogFactory.createIAmA(internalVisualizer) + dialogFactory.createIAmA() } override fun createIAmAPaperVisionDialog(showWorkspacesButton: Boolean) = apiImpl { - DialogFactory.createIAmAPaperVision(internalVisualizer, showWorkspacesButton) + dialogFactory.createIAmAPaperVision(showWorkspacesButton) } override fun createWorkspaceDialog() = apiImpl { - DialogFactory.createWorkspace(internalVisualizer) + dialogFactory.createWorkspace() } override fun createCrashReportDialog(report: String) = apiImpl { - DialogFactory.createCrashReport(internalVisualizer, report) + dialogFactory.createCrashReport(report) } override fun disableApi() { } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt index fd2fce6a..059a32ff 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt @@ -33,16 +33,35 @@ import io.github.deltacv.eocvsim.plugin.api.PipelineManagerApi import io.github.deltacv.eocvsim.plugin.api.VariableTunerApi import io.github.deltacv.eocvsim.plugin.api.VisualizerApi -class EOCVSimApiImpl(owner: EOCVSimPlugin, val internalEOCVSim: EOCVSim) : EOCVSimApi(owner) { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.gui.Visualizer +import com.github.serivesmejia.eocvsim.input.InputSourceManager +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import com.github.serivesmejia.eocvsim.tuner.TunerManager +import com.github.serivesmejia.eocvsim.config.ConfigManager + +class EOCVSimApiImpl(owner: EOCVSimPlugin) : EOCVSimApi(owner), KoinComponent { private val logger by loggerForThis() - override val mainLoopHook by apiField { EventHandlerHookApiImpl(owner, internalEOCVSim.onMainUpdate) } + private val onMainUpdate: EventHandler by inject(named("onMainLoop")) + private val visualizer: Visualizer by inject() + private val inputSourceManager: InputSourceManager by inject() + private val pipelineManager: PipelineManager by inject() + private val tunerManager: TunerManager by inject() + private val configManager: ConfigManager by inject() + + val internalEOCVSim: EOCVSim by inject() + + override val mainLoopHook by apiField { EventHandlerHookApiImpl(owner, onMainUpdate) } - override val visualizerApi: VisualizerApi by apiField(VisualizerApiImpl(owner, internalEOCVSim.visualizer)) - override val inputSourceManagerApi: InputSourceManagerApi by apiField(InputSourceManagerApiImpl(owner, internalEOCVSim.inputSourceManager)) - override val pipelineManagerApi: PipelineManagerApi by apiField(PipelineManagerApiImpl(owner, internalEOCVSim.pipelineManager)) - override val variableTunerApi: VariableTunerApi by apiField(VariableTunerApiImpl(owner, internalEOCVSim.tunerManager)) - override val configApi: ConfigApi by apiField(ConfigApiImpl(owner, internalEOCVSim.configManager)) + override val visualizerApi: VisualizerApi by apiField(VisualizerApiImpl(owner, visualizer)) + override val inputSourceManagerApi: InputSourceManagerApi by apiField(InputSourceManagerApiImpl(owner, inputSourceManager)) + override val pipelineManagerApi: PipelineManagerApi by apiField(PipelineManagerApiImpl(owner, pipelineManager)) + override val variableTunerApi: VariableTunerApi by apiField(VariableTunerApiImpl(owner, tunerManager)) + override val configApi: ConfigApi by apiField(ConfigApiImpl(owner, configManager)) override fun disableApi() { logger.info("EOCV-Sim API for {} says: \"ight, imma head out\"", ownerName) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt index a05a30ab..af7ca257 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt @@ -33,7 +33,7 @@ import io.github.deltacv.eocvsim.plugin.api.InputSourceApi import io.github.deltacv.eocvsim.plugin.api.InputSourceManagerApi class InputSourceApiImpl(owner: EOCVSimPlugin, val internalInputSource: com.github.serivesmejia.eocvsim.input.InputSource) : InputSourceApi(owner) { - override val isPaused by liveApiField { internalInputSource.paused } + override val isPaused by liveApiField { internalInputSource.isPaused } override val data by apiField { when(internalInputSource) { @@ -46,18 +46,20 @@ class InputSourceApiImpl(owner: EOCVSimPlugin, val internalInputSource: com.gith } override val name: String by apiField { internalInputSource.name } - override val creationTime by apiField { internalInputSource.creationTime } + override val creationTime by apiField { internalInputSource.creationTime } + + override fun disableApi() { } } class InputSourceManagerApiImpl(owner: EOCVSimPlugin, val internalInputSourceManager: InputSourceManager) : InputSourceManagerApi(owner) { - override val allSources: List by liveApiField { + override val allSources: List by liveApiField> { internalInputSourceManager.sources.values.map { InputSourceApiImpl(owner, it) } } - override val currentSource: InputSourceApi? by liveNullableApiField { + override val currentSource: InputSourceApi? by liveNullableApiField { internalInputSourceManager.currentInputSource?.let { InputSourceApiImpl(owner, it) } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.kt index 1465926a..e5a36e46 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.kt @@ -29,12 +29,18 @@ import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanelConf import com.github.serivesmejia.eocvsim.util.event.EventHandler import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import javax.swing.SwingUtilities + abstract class TunableField( protected val target: Any, val reflectionField: VirtualField, - protected val eocvSim: EOCVSim, val allowMode: AllowMode = AllowMode.TEXT -) { +) : KoinComponent { + + protected val eocvSim: EOCVSim by inject() + var fieldPanel: TunableFieldPanel? = null private set @@ -72,7 +78,7 @@ abstract class TunableField( for ((index, tunableValue) in tunableValues.withIndex()) { tunableValue.onPipelineUpdate.attach { if (!isIgnoreGuiUpdates) { - javax.swing.SwingUtilities.invokeLater { + SwingUtilities.invokeLater { fieldPanel.setFieldValue(index, tunableValue.value) } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldRegistry.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldRegistry.kt index fa8653eb..7a4f8866 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldRegistry.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldRegistry.kt @@ -36,34 +36,34 @@ import com.github.serivesmejia.eocvsim.tuner.field.cv.* */ object TunableFieldRegistry { - private val exactFields = mutableMapOf, (Any, VirtualField, EOCVSim) -> TunableField<*>>() - private val acceptors = mutableListOf TunableField<*>>>() + private val exactFields = mutableMapOf, (Any, VirtualField) -> TunableField<*>>() + private val acceptors = mutableListOf TunableField<*>>>() init { - registerField(Int::class.javaObjectType) { target, f, sim -> IntegerField(target, f, sim) } - registerField(Double::class.javaObjectType) { target, f, sim -> DoubleField(target, f, sim) } - registerField(Float::class.javaObjectType) { target, f, sim -> FloatField(target, f, sim) } - registerField(Long::class.javaObjectType) { target, f, sim -> LongField(target, f, sim) } + registerField(Int::class.javaObjectType) { target, f -> IntegerField(target, f) } + registerField(Double::class.javaObjectType) { target, f -> DoubleField(target, f) } + registerField(Float::class.javaObjectType) { target, f -> FloatField(target, f) } + registerField(Long::class.javaObjectType) { target, f -> LongField(target, f) } - registerField(String::class.java) { target, f, sim -> StringField(target, f, sim) } - registerField(Boolean::class.javaObjectType) { target, f, sim -> BooleanField(target, f, sim) } + registerField(String::class.java) { target, f -> StringField(target, f) } + registerField(Boolean::class.javaObjectType) { target, f -> BooleanField(target, f) } - registerField(org.opencv.core.Scalar::class.java) { target, f, sim -> ScalarField(target, f, sim) } - registerField(org.opencv.core.Point::class.java) { target, f, sim -> PointField(target, f, sim) } - registerField(org.opencv.core.Rect::class.java) { target, f, sim -> RectField(target, f, sim) } + registerField(org.opencv.core.Scalar::class.java) { target, f -> ScalarField(target, f) } + registerField(org.opencv.core.Point::class.java) { target, f -> PointField(target, f) } + registerField(org.opencv.core.Rect::class.java) { target, f -> RectField(target, f) } - registerAcceptor(EnumField.Acceptor()) { target, f, sim -> EnumField(target, f, sim) } + registerAcceptor(EnumField.Acceptor()) { target, f -> EnumField(target, f) } } - fun registerField(type: Class<*>, constructor: (Any, VirtualField, EOCVSim) -> TunableField<*>) { + fun registerField(type: Class<*>, constructor: (Any, VirtualField) -> TunableField<*>) { exactFields[type] = constructor } - fun registerAcceptor(acceptor: TunableFieldAcceptor, constructor: (Any, VirtualField, EOCVSim) -> TunableField<*>) { + fun registerAcceptor(acceptor: TunableFieldAcceptor, constructor: (Any, VirtualField) -> TunableField<*>) { acceptors.add(acceptor to constructor) } - fun getTunableFieldFor(field: VirtualField, pipeline: Any, eocvSim: EOCVSim): TunableField<*>? { + fun getTunableFieldFor(field: VirtualField, pipeline: Any): TunableField<*>? { if (field.isFinal) return null var type = field.type @@ -76,9 +76,10 @@ object TunableFieldRegistry { constructor = acceptors.find { it.first.accept(type) }?.second } - return constructor?.invoke(pipeline, field, eocvSim) + return constructor?.invoke(pipeline, field) } + fun hasTunableFieldFor(type: Class<*>): Boolean { var t = type if (t.isPrimitive) { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableValue.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableValue.kt index cebf0a10..87de7a41 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableValue.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableValue.kt @@ -25,7 +25,7 @@ package com.github.serivesmejia.eocvsim.tuner import com.github.serivesmejia.eocvsim.util.event.EventHandler -sealed class TunableValue( +sealed class TunableValue( initialValue: T, val supplier: () -> T, val consumer: (T) -> Unit diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt index 08c539f5..d5446dc5 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt @@ -1,6 +1,5 @@ package com.github.serivesmejia.eocvsim.tuner -import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel import com.github.serivesmejia.eocvsim.tuner.exception.CancelTunableFieldAddingException import io.github.deltacv.common.util.loggerForThis @@ -8,7 +7,17 @@ import io.github.deltacv.eocvsim.virtualreflect.VirtualField import io.github.deltacv.eocvsim.virtualreflect.VirtualReflection import io.github.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflection -class TunerManager(private val eocvSim: EOCVSim) { +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import com.github.serivesmejia.eocvsim.gui.Visualizer + +class TunerManager : KoinComponent { + + val pipelineManager: PipelineManager by inject() + + val visualizer: Visualizer by inject() + val logger by loggerForThis() @@ -20,13 +29,13 @@ class TunerManager(private val eocvSim: EOCVSim) { fun init() { if (firstInit) { - eocvSim.pipelineManager.onPipelineChange.attach { reset() } + pipelineManager.onPipelineChange.attach { reset() } firstInit = false } - eocvSim.pipelineManager.reflectTarget?.let { target -> + pipelineManager.reflectTarget?.let { target -> addFieldsFrom(target) - eocvSim.visualizer.updateTunerFields(createTunableFieldPanels()) + visualizer.updateTunerFields(createTunableFieldPanels()) val iterator = fields.iterator() while (iterator.hasNext()) { @@ -63,7 +72,7 @@ class TunerManager(private val eocvSim: EOCVSim) { } fun newTunableFieldInstanceFor(field: VirtualField, pipeline: Any): TunableField<*>? { - return TunableFieldRegistry.getTunableFieldFor(field, pipeline, eocvSim) + return TunableFieldRegistry.getTunableFieldFor(field, pipeline) } fun addFieldsFrom(pipeline: Any) { @@ -91,6 +100,7 @@ class TunerManager(private val eocvSim: EOCVSim) { } private fun createTunableFieldPanels(): List { - return fields.map { TunableFieldPanel(it, eocvSim) } + return fields.map { TunableFieldPanel(it) } } + } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.kt index bdeb0243..da649174 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.kt @@ -7,9 +7,9 @@ import io.github.deltacv.eocvsim.virtualreflect.VirtualField class BooleanField( instance: Any, - reflectionField: VirtualField, - eocvSim: EOCVSim -) : TunableField(instance, reflectionField, eocvSim, AllowMode.TEXT) { + reflectionField: VirtualField +) : TunableField(instance, reflectionField, AllowMode.TEXT) { + private var _value: Boolean = initialFieldValue as? Boolean ?: false diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/EnumField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/EnumField.kt index 6a6bc781..c7a4a1db 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/EnumField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/EnumField.kt @@ -7,8 +7,8 @@ import com.github.serivesmejia.eocvsim.tuner.TunableFieldAcceptor import io.github.deltacv.eocvsim.virtualreflect.VirtualField class EnumField(instance: Any, - reflectionField: VirtualField, - eocvSim: EOCVSim) : TunableField>(instance, reflectionField, eocvSim, AllowMode.TEXT) { + reflectionField: VirtualField) : TunableField>(instance, reflectionField, AllowMode.TEXT) { + val values = reflectionField.type.enumConstants diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt index 3ebe2da7..c368dde5 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt @@ -9,10 +9,10 @@ import io.github.deltacv.eocvsim.virtualreflect.VirtualField abstract class NumericField( target: Any, reflectionField: VirtualField, - eocvSim: EOCVSim, allowMode: AllowMode, initialValue: T -) : TunableField(target, reflectionField, eocvSim, allowMode) { +) : TunableField(target, reflectionField, allowMode) { + protected var _value: T = initialValue diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.kt index 4323a9db..370a394a 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.kt @@ -8,9 +8,9 @@ import io.github.deltacv.eocvsim.virtualreflect.VirtualField class StringField( instance: Any, - reflectionField: VirtualField, - eocvSim: EOCVSim -) : TunableField(instance, reflectionField, eocvSim, AllowMode.TEXT) { + reflectionField: VirtualField +) : TunableField(instance, reflectionField, AllowMode.TEXT) { + private var _value: String = initialFieldValue as? String ?: "" diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.kt index d1d95597..b75099a8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.kt @@ -8,9 +8,9 @@ import org.opencv.core.Point class PointField( instance: Any, - reflectionField: VirtualField, - eocvSim: EOCVSim -) : TunableField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL) { + reflectionField: VirtualField +) : TunableField(instance, reflectionField, AllowMode.ONLY_NUMBERS_DECIMAL) { + private var point: Point = if (initialFieldValue != null) { val p = initialFieldValue as Point diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/RectField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/RectField.kt index 5916f570..2acb8e59 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/RectField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/RectField.kt @@ -29,8 +29,9 @@ import com.github.serivesmejia.eocvsim.tuner.TunableNumber import org.opencv.core.Rect import io.github.deltacv.eocvsim.virtualreflect.VirtualField -class RectField(instance: Any, reflectionField: VirtualField, eocvSim: EOCVSim) : - TunableField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL) { +class RectField(instance: Any, reflectionField: VirtualField) : + TunableField(instance, reflectionField, AllowMode.ONLY_NUMBERS_DECIMAL) { + private var rect = arrayOf(0.0, 0.0, 0.0, 0.0) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.kt index c8b9f6d1..412ac51a 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.kt @@ -9,9 +9,9 @@ import org.opencv.core.Scalar class ScalarField( instance: Any, - reflectionField: VirtualField, - eocvSim: EOCVSim -) : TunableField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL) { + reflectionField: VirtualField +) : TunableField(instance, reflectionField, AllowMode.ONLY_NUMBERS_DECIMAL) { + private var scalar: Scalar = if (initialFieldValue == null) { Scalar(0.0, 0.0, 0.0) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt index 0651ce6f..6314f65d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt @@ -7,9 +7,9 @@ import com.github.serivesmejia.eocvsim.tuner.TunableFieldAcceptor class DoubleField( instance: Any, - reflectionField: VirtualField, - eocvSim: EOCVSim -) : NumericField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL, reflectionField.get() as? Double ?: 0.0) { + reflectionField: VirtualField +) : NumericField(instance, reflectionField, AllowMode.ONLY_NUMBERS_DECIMAL, reflectionField.get() as? Double ?: 0.0) { + override fun createNumber(value: Double): Double = value diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt index 87ed044e..ce622c58 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt @@ -7,9 +7,9 @@ import io.github.deltacv.eocvsim.virtualreflect.VirtualField class FloatField( instance: Any, - reflectionField: VirtualField, - eocvSim: EOCVSim -) : NumericField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS_DECIMAL, reflectionField.get() as? Float ?: 0.0f) { + reflectionField: VirtualField +) : NumericField(instance, reflectionField, AllowMode.ONLY_NUMBERS_DECIMAL, reflectionField.get() as? Float ?: 0.0f) { + override fun createNumber(value: Double): Float = value.toFloat() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt index 078eb065..9b03f7d8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt @@ -7,9 +7,9 @@ import com.github.serivesmejia.eocvsim.tuner.TunableFieldAcceptor class IntegerField( instance: Any, - reflectionField: VirtualField, - eocvSim: EOCVSim -) : NumericField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS, reflectionField.get() as? Int ?: 0) { + reflectionField: VirtualField +) : NumericField(instance, reflectionField, AllowMode.ONLY_NUMBERS, reflectionField.get() as? Int ?: 0) { + override fun createNumber(value: Double): Int = value.toInt() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt index 9d95de87..4e831c44 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt @@ -7,9 +7,9 @@ import io.github.deltacv.eocvsim.virtualreflect.VirtualField class LongField( instance: Any, - reflectionField: VirtualField, - eocvSim: EOCVSim -) : NumericField(instance, reflectionField, eocvSim, AllowMode.ONLY_NUMBERS, reflectionField.get() as? Long ?: 0L) { + reflectionField: VirtualField +) : NumericField(instance, reflectionField, AllowMode.ONLY_NUMBERS, reflectionField.get() as? Long ?: 0L) { + override fun createNumber(value: Double): Long = value.toLong() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt index 9227a281..b3d784f3 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt @@ -59,7 +59,10 @@ class ClasspathScan { val logger by loggerForThis() - lateinit var scanResult: ScanResult + var scanResult: ScanResult? = null + private set + + var hasScanned = false private set private lateinit var scanResultJob: Job @@ -166,7 +169,7 @@ class ClasspathScan { pipelineClasses.toTypedArray() ) - return this.scanResult + return this.scanResult!! } /** @@ -175,6 +178,9 @@ class ClasspathScan { */ @OptIn(DelicateCoroutinesApi::class) fun asyncScan(scope: CoroutineScope = GlobalScope) { + if(hasScanned) return + hasScanned = true + scanResultJob = scope.launch(Dispatchers.IO) { scan() } @@ -185,7 +191,9 @@ class ClasspathScan { * @see asyncScan */ fun join() = runBlocking { - scanResultJob.join() + if(::scanResultJob.isInitialized) { + scanResultJob.join() + } } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReportOutputMain.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReportOutputMain.kt index 0f92e917..0317014c 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReportOutputMain.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReportOutputMain.kt @@ -17,7 +17,8 @@ object CrashReportOutputMain { override fun run() { SwingUtilities.invokeLater(FlatArcDarkIJTheme::setup) - DialogFactory.createCrashReport(null, SysUtil.loadFileStr(File(crashReportPath ?: ""))) + // TODO: Make this actually work, need to setup koin + // dialogFactory.createCrashReport(null, SysUtil.loadFileStr(File(crashReportPath ?: ""))) } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt index eaafc921..9f223141 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt @@ -24,19 +24,25 @@ package com.github.serivesmejia.eocvsim.workspace import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.config.ConfigManager +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.pipeline.compiler.CompiledPipelineManager +import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.util.io.FileWatcher -import com.github.serivesmejia.eocvsim.util.SysUtil -import io.github.deltacv.common.util.loggerForThis import com.github.serivesmejia.eocvsim.workspace.config.WorkspaceConfig import com.github.serivesmejia.eocvsim.workspace.config.WorkspaceConfigLoader import com.github.serivesmejia.eocvsim.workspace.util.WorkspaceTemplate +import io.github.deltacv.common.util.loggerForThis +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named import java.io.File +import java.nio.file.Path import java.nio.file.Paths /** @@ -52,11 +58,16 @@ import java.nio.file.Paths * excluded paths and file extensions, and the source files themselves * which are built using an embedded compiler and loaded with a custom * classloader. - * - * @param eocvSim the EOCVSim instance to manage the workspace for */ @OptIn(DelicateCoroutinesApi::class) -class WorkspaceManager(val eocvSim: EOCVSim) { +class WorkspaceManager : KoinComponent { + + private val pipelineManager: PipelineManager by inject() + private val compiledPipelineManager: CompiledPipelineManager by inject() + private val configManager: ConfigManager by inject() + private val params: EOCVSim.Parameters by inject() + private val scope: CoroutineScope by inject() + private val onMainLoop: EventHandler by inject(named("onMainLoop")) val logger by loggerForThis() @@ -70,17 +81,17 @@ class WorkspaceManager(val eocvSim: EOCVSim) { */ var workspaceFile = File(".") set(value) { - if(value != workspaceFile) { + if (value != workspaceFile) { workspaceConfigLoader.workspaceFile = value - eocvSim.config.workspacePath = value.absolutePath - eocvSim.configManager.saveToFile() + configManager.config.workspacePath = value.absolutePath + configManager.saveToFile() field = value logger.info("Set current workspace to ${value.absolutePath}") - if(::fileWatcher.isInitialized) + if (::fileWatcher.isInitialized) fileWatcher.stop() fileWatcher = FileWatcher( @@ -96,10 +107,10 @@ class WorkspaceManager(val eocvSim: EOCVSim) { cachedWorkspConfig = workspaceConfigLoader.loadWorkspaceConfig() - if(cachedWorkspConfig == null) { + if (cachedWorkspConfig == null) { cachedWorkspConfig = WorkspaceConfig() - if(workspaceConfigLoader.workspaceConfigFile.exists()) + if (workspaceConfigLoader.workspaceConfigFile.exists()) logger.warn("Recreating workspace config file, old one failed to parse") else logger.info("Creating workspace config file...") @@ -122,7 +133,7 @@ class WorkspaceManager(val eocvSim: EOCVSim) { cachedWorkspConfig = value } get() { - if(cachedWorkspConfig == null) + if (cachedWorkspConfig == null) ::workspaceFile.set(workspaceFile) return cachedWorkspConfig!! @@ -132,57 +143,63 @@ class WorkspaceManager(val eocvSim: EOCVSim) { * The relative path to the sources folder specified in the workspace configuration */ val sourcesRelativePath get() = workspaceConfig.sourcesPath!! + /** * The absolute path to the sources folder specified in the workspace configuration */ - val sourcesAbsolutePath get() = Paths.get(workspaceFile.absolutePath, sourcesRelativePath).normalize()!! + val sourcesAbsolutePath: Path get() = Paths.get(workspaceFile.absolutePath, sourcesRelativePath).normalize() /** * The relative path to the resources folder specified in the workspace configuration */ val resourcesRelativePath get() = workspaceConfig.resourcesPath!! + /** * The absolute path to the resources folder specified in the workspace configuration */ - val resourcesAbsolutePath get() = Paths.get(workspaceFile.absolutePath, resourcesRelativePath).normalize()!! + val resourcesAbsolutePath: Path get() = Paths.get(workspaceFile.absolutePath, resourcesRelativePath).normalize() /** * The relative paths to the excluded paths specified in the workspace configuration */ - val excludedRelativePaths get() = workspaceConfig.excludedPaths + val excludedRelativePaths: List get() = workspaceConfig.excludedPaths + /** * The absolute paths to the excluded paths specified in the workspace configuration */ - val excludedAbsolutePaths get() = excludedRelativePaths.map { - Paths.get(workspaceFile.absolutePath, it).normalize()!! - } + val excludedAbsolutePaths + get() = excludedRelativePaths.map { + Paths.get(workspaceFile.absolutePath, it).normalize() + } /** * The file extensions to exclude from the workspace */ - val excludedFileExtensions get() = workspaceConfig.excludedFileExtensions + val excludedFileExtensions: ArrayList get() = workspaceConfig.excludedFileExtensions /** * The source files in the workspace, excluding the excluded paths and file extensions */ - val sourceFiles get() = SysUtil.filesUnder(sourcesAbsolutePath.toFile()) { file -> - file.name.endsWith(".java") && excludedAbsolutePaths.stream().noneMatch { - file.startsWith(it.toFile().absolutePath) + val sourceFiles: List + get() = SysUtil.filesUnder(sourcesAbsolutePath.toFile()) { file -> + file.name.endsWith(".java") && excludedAbsolutePaths.stream().noneMatch { + file.startsWith(it.toFile().absolutePath) + } } - } /** * The resource files in the workspace, excluding the excluded paths and file extensions */ - val resourceFiles get() = SysUtil.filesUnder(resourcesAbsolutePath.toFile()) { file -> - file.name.run { - !endsWith(".java") && !endsWith(".class") && this != "eocvsim_workspace.json" - } && excludedAbsolutePaths.stream().noneMatch { - file.startsWith(it.toFile().absolutePath) - } && excludedFileExtensions.stream().noneMatch { - file.name.endsWith(".$it") + val resourceFiles: List + get() = SysUtil.filesUnder(resourcesAbsolutePath.toFile()) { file -> + file.name.run { + !endsWith(".java") && !endsWith(".class") && this != "eocvsim_workspace.json" + } && excludedAbsolutePaths.stream().noneMatch { + file.startsWith(it.toFile().absolutePath) + } && excludedFileExtensions.stream().noneMatch { + file.name.endsWith(".$it") + } } - } /** * Event handler to run code when the workspace changes @@ -199,7 +216,7 @@ class WorkspaceManager(val eocvSim: EOCVSim) { * Stops the current file watcher, if initialized */ fun stopFileWatcher() { - if(::fileWatcher.isInitialized) { + if (::fileWatcher.isInitialized) { fileWatcher.stop() } } @@ -213,8 +230,8 @@ class WorkspaceManager(val eocvSim: EOCVSim) { * @return true if the workspace was created successfully, false otherwise */ fun createWorkspaceWithTemplate(folder: File, template: WorkspaceTemplate): Boolean { - if(!folder.isDirectory) return false - if(!template.extractToIfEmpty(folder)) return false + if (!folder.isDirectory) return false + if (!template.extractToIfEmpty(folder)) return false workspaceFile = folder return true @@ -226,19 +243,20 @@ class WorkspaceManager(val eocvSim: EOCVSim) { * Runs asynchronously on a coroutine * @param folder the folder to create the workspace in */ - @JvmOverloads fun createWorkspaceWithTemplateAsync( + @JvmOverloads + fun createWorkspaceWithTemplateAsync( folder: File, template: WorkspaceTemplate, finishCallback: (() -> Unit)? = null - ) = eocvSim.scope.launch(Dispatchers.IO) { - if(!folder.isDirectory) return@launch - if(!template.extractToIfEmpty(folder)) return@launch + ) = scope.launch(Dispatchers.IO) { + if (!folder.isDirectory) return@launch + if (!template.extractToIfEmpty(folder)) return@launch - eocvSim.onMainUpdate.once { + onMainLoop.once { workspaceFile = folder - if(finishCallback != null) finishCallback() + if (finishCallback != null) finishCallback() - eocvSim.pipelineManager.compiledPipelineManager.asyncBuild() + compiledPipelineManager.asyncBuild() } } @@ -251,13 +269,13 @@ class WorkspaceManager(val eocvSim: EOCVSim) { fun init() { onWorkspaceChange { fileWatcher.onChange { - eocvSim.pipelineManager.compiledPipelineManager.asyncBuild() + pipelineManager.compiledPipelineManager.asyncBuild() } } - val file = eocvSim.params.initialWorkspace ?: File(eocvSim.config.workspacePath) + val file = params.initialWorkspace ?: File(configManager.config.workspacePath) - workspaceFile = if(file.exists()) + workspaceFile = if (file.exists()) file else CompiledPipelineManager.DEF_WORKSPACE_FOLDER diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSource.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSource.kt index dd31747b..751306d4 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSource.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSource.kt @@ -31,7 +31,7 @@ class VisionInputSource( return true } - override fun startSource(size: Size?): Boolean { + override fun startSource(size: Size): Boolean { inputSource.setSize(size) inputSource.init() return true diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt index 95dafc14..fc85b2b2 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt @@ -43,9 +43,9 @@ class VisionInputSourceProvider( override fun get(name: String): VisionSource { val source = VisionInputSource(if(File(name).exists()) { if(isImage(name)) { - ImageSource(name) + ImageSource(name, Size()) } else if(isVideo(name)) { - VideoSource(name, null) + VideoSource(name, Size()) } else throw IllegalArgumentException("File $name is neither an image nor a video") } else { val index = name.toIntOrNull() @@ -53,7 +53,7 @@ class VisionInputSourceProvider( else null if(index == null) { - inputSourceManager.sources.get(name) ?: throw IllegalArgumentException("Input source $name not found") + inputSourceManager.sources[name] ?: throw IllegalArgumentException("Input source $name not found") } else CameraSource(index, Size(640.0, 480.0)) }, RedirectToOpModeThrowableHandler(notifier)) diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt index 02a2dd0c..caec1a66 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt @@ -10,7 +10,7 @@ class CameraSourceExposureControl( cameraSource: CameraSource ) : ExposureControl { - val control = cameraSource.webcamPropertyControl + val control = cameraSource.getWebcamPropertyControl()!! override fun getMode() = if(control.getPropertyAuto(WebcamProperty.EXPOSURE)) { ExposureControl.Mode.Auto 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 a3005239..0b88e9dd 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 @@ -41,11 +41,23 @@ import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock import kotlin.properties.Delegates +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import com.github.serivesmejia.eocvsim.config.ConfigManager +import com.github.serivesmejia.eocvsim.gui.Visualizer +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import org.koin.core.qualifier.named + /** * Manages the loading, enabling and disabling of plugins - * @param eocvSim the EOCV-Sim instance */ -class PluginManager(val eocvSim: EOCVSim) { +class PluginManager : KoinComponent { + + private val configManager: ConfigManager by inject() + private val visualizer: Visualizer by inject() + private val dialogFactory: DialogFactory by inject() + private val onMainUpdate: EventHandler by inject(named("onMainLoop")) + private val onRestartRequested: EventHandler by inject(named("onRestartRequested")) companion object { val PLUGIN_FOLDER = io.github.deltacv.eocvsim.plugin.PLUGIN_FOLDER @@ -62,7 +74,7 @@ class PluginManager(val eocvSim: EOCVSim) { val superAccessDaemonClient by lazy { SuperAccessDaemonClient( - autoacceptOnTrusted = eocvSim.config.autoAcceptSuperAccessOnTrusted + autoacceptOnTrusted = configManager.config.autoAcceptSuperAccessOnTrusted ) } @@ -72,7 +84,7 @@ class PluginManager(val eocvSim: EOCVSim) { private val haltCondition = haltLock.newCondition() val appender by lazy { - val appender = DialogFactory.createMavenOutput(this) { + val appender = dialogFactory.createMavenOutput { haltLock.withLock { haltCondition.signalAll() } @@ -94,7 +106,7 @@ class PluginManager(val eocvSim: EOCVSim) { } val repositoryManager by lazy { - PluginRepositoryManager(appender, eocvSim, haltLock, haltCondition) + PluginRepositoryManager(appender, onMainUpdate, { onRestartRequested.run() }, haltLock, haltCondition) } private val _pluginFiles = mutableListOf() @@ -113,7 +125,7 @@ class PluginManager(val eocvSim: EOCVSim) { /** * Provides EOCV-Sim API instances for plugins */ - val eocvSimApiProvider = EOCVSimApiProvider { plugin -> EOCVSimApiImpl(plugin, eocvSim) } + val eocvSimApiProvider = EOCVSimApiProvider { plugin -> EOCVSimApiImpl(plugin) } /** * Initializes the plugin manager @@ -123,7 +135,7 @@ class PluginManager(val eocvSim: EOCVSim) { * @see PluginLoader */ fun init() { - eocvSim.visualizer.onInitFinished { + visualizer.onInitFinished { appender.append(PluginOutput.SPECIAL_FREE) } @@ -133,7 +145,7 @@ class PluginManager(val eocvSim: EOCVSim) { // replace papervision line - if (!eocvSim.config.flags.getOrDefault("hasDiscardedPaperVisionRepository", false)) { + if (!configManager.config.flags.getOrDefault("hasDiscardedPaperVisionRepository", false)) { try { val repositoriesStr = PluginRepositoryManager.REPOSITORY_FILE.readText() for (line in repositoriesStr.lines()) { @@ -154,7 +166,7 @@ class PluginManager(val eocvSim: EOCVSim) { } catch (_: Exception) { } - eocvSim.config.flags["hasDiscardedPaperVisionRepository"] = true + configManager.config.flags["hasDiscardedPaperVisionRepository"] = true } repositoryManager.init() @@ -165,15 +177,15 @@ class PluginManager(val eocvSim: EOCVSim) { _pluginFiles.addAll(repositoryManager.resolveAll()) - if (eocvSim.config.flags.getOrDefault("startFresh", false)) { + if (configManager.config.flags.getOrDefault("startFresh", false)) { logger.warn("startFresh = true, deleting all plugins in the plugins folder") for (file in pluginFilesInFolder) { file.delete() } - eocvSim.config.flags["startFresh"] = false - eocvSim.configManager.saveToFile() + configManager.config.flags["startFresh"] = false + configManager.saveToFile() } else { _pluginFiles.addAll(pluginFilesInFolder) } @@ -260,7 +272,6 @@ class PluginManager(val eocvSim: EOCVSim) { */ fun loadPlugins() { for (loader in _loaders.toTypedArray()) { - try { val hash = loader.hash() @@ -376,10 +387,10 @@ class PluginManager(val eocvSim: EOCVSim) { fun hasSuperAccess(pluginFile: File) = superAccessDaemonClient.checkAccess(pluginFile) - fun isPluginEnabledInConfig(loader: PluginLoader) = eocvSim.config.flags.getOrDefault(loader.hash(), true)!! + fun isPluginEnabledInConfig(loader: PluginLoader) = configManager.config.flags.getOrDefault(loader.hash(), true)!! fun setPluginEnabledInConfig(loader: PluginLoader, enabled: Boolean) { - eocvSim.config.flags[loader.hash()] = enabled - eocvSim.configManager.saveToFile() + configManager.config.flags[loader.hash()] = enabled + configManager.saveToFile() } } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt index b235b9be..e9945014 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt @@ -43,9 +43,12 @@ import javax.swing.JOptionPane import kotlin.concurrent.withLock +import com.github.serivesmejia.eocvsim.util.event.EventHandler + class PluginRepositoryManager( val appender: AppendDelegate, - val eocvSim: EOCVSim, + val onMainUpdate: EventHandler, + val restart: () -> Unit, val haltLock: ReentrantLock, val haltCondition: Condition ) { @@ -155,7 +158,7 @@ class PluginRepositoryManager( "Plugin \"${plugin.key}\" is outdated. Latest version is ${latest.version}." ) - eocvSim.onMainUpdate.once { + onMainUpdate.once { promptUpdateAndRestart(plugin.key, pluginDep, latest) } } else { @@ -211,7 +214,7 @@ class PluginRepositoryManager( tomlFile.writeText(tomlLines.joinToString("\n")) appender.appendln(PluginOutput.SPECIAL_SILENT +"Successfully updated \"$pluginName\" to version ${latest.version}. Restarting...") - eocvSim.restart() + restart() } } diff --git a/build.gradle b/build.gradle index 47e49208..2f8c247a 100644 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,7 @@ buildscript { ext { kotlin_version = "2.3.0" kotlinx_coroutines_version = "1.10.2" + koin_version = "4.2.0" slf4j_version = "2.0.16" log4j_version = "2.24.1" opencv_version = "4.7.0-0" From f73436f9acd581a84b59f68c145c863a0d352daa Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Thu, 23 Apr 2026 18:31:55 -0600 Subject: [PATCH 08/40] Redesign manager initialization into orchestrator to paralellize & decentralize --- Common/build.gradle | 3 + .../eocvsim/util/event/EventHandler.kt | 88 +++-- .../eocvsim/util/event/Orchestrator.kt | 183 ++++++++++ EOCV-Sim/build.gradle | 327 +++++++++--------- .../github/serivesmejia/eocvsim/EOCVSim.kt | 188 +++------- .../github/serivesmejia/eocvsim/Lifecycle.kt | 8 + .../com/github/serivesmejia/eocvsim/Main.kt | 16 +- .../{di/EOCVSimModule.kt => Module.kt} | 117 +++---- .../eocvsim/config/ConfigManager.kt | 19 +- .../serivesmejia/eocvsim/gui/DialogFactory.kt | 15 +- .../serivesmejia/eocvsim/gui/Visualizer.kt | 111 +++++- .../gui/component/visualizer/SidebarPanel.kt | 15 +- .../gui/component/visualizer/TopMenuBar.kt | 10 +- .../eocvsim/gui/dialog/PluginOutput.kt | 24 +- .../eocvsim/input/InputSourceInitializer.kt | 5 - .../eocvsim/input/InputSourceManager.kt | 17 +- .../eocvsim/pipeline/PipelineManager.kt | 41 ++- .../compiler/CompiledPipelineManager.kt | 26 +- .../eocvsim/plugin/api/impl/EOCVSimApiImpl.kt | 2 +- .../eocvsim/tuner/TunerManager.kt | 29 +- .../eocvsim/util/ClasspathScan.kt | 84 ++--- .../eocvsim/workspace/WorkspaceManager.kt | 55 +-- .../eocvsim/plugin/loader/PluginManager.kt | 25 +- .../startup/StartupOrchestratorTest.kt | 0 .../external/gui/SwingOpenCvViewport.kt | 8 +- 25 files changed, 848 insertions(+), 568 deletions(-) create mode 100644 Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/Orchestrator.kt create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Lifecycle.kt rename EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/{di/EOCVSimModule.kt => Module.kt} (50%) create mode 100644 EOCV-Sim/src/test/java/com/github/serivesmejia/eocvsim/startup/StartupOrchestratorTest.kt diff --git a/Common/build.gradle b/Common/build.gradle index 654472dc..ebfd202f 100644 --- a/Common/build.gradle +++ b/Common/build.gradle @@ -27,7 +27,9 @@ dependencies { 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' + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinx_coroutines_version" implementation "com.formdev:flatlaf:$flatlaf_version" implementation "com.miglayout:miglayout-swing:$miglayout_version" @@ -41,4 +43,5 @@ dependencies { implementation("org.jetbrains.skiko:skiko-awt-runtime-linux-arm64:$skiko_version") implementation("org.jetbrains.skiko:skiko-awt-runtime-macos-x64:$skiko_version") implementation("org.jetbrains.skiko:skiko-awt-runtime-macos-arm64:$skiko_version") + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2" } \ No newline at end of file diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventHandler.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventHandler.kt index afff697a..bd669cc1 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventHandler.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventHandler.kt @@ -1,6 +1,11 @@ package com.github.serivesmejia.eocvsim.util.event import io.github.deltacv.common.util.loggerOf +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.job +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import java.util.concurrent.atomic.AtomicInteger /** @@ -8,7 +13,11 @@ import java.util.concurrent.atomic.AtomicInteger * - Persistent listeners (ID-based, queue-based adding) * - Once listeners (double buffered queue-based, deferred) */ -class EventHandler(val name: String) : Runnable { +class EventHandler @JvmOverloads constructor( + val name: String, + var callRightAway: CallRightAway = CallRightAway.Disabled, + val catchExceptions: Boolean = true +) : Runnable { // ------------------------------------------------------------ // config @@ -16,8 +25,6 @@ class EventHandler(val name: String) : Runnable { private val logger by loggerOf("EventHandler-$name") - var callRightAway = false - // ------------------------------------------------------------ // ids // ------------------------------------------------------------ @@ -76,14 +83,22 @@ class EventHandler(val name: String) : Runnable { } } + fun run(id: Int, listener: EventListener) { + val remover = persistentContextCache.getOrPut(id) { EventListenerContext(this, EventListenerId(id)) } + listener(remover) + } + // execute for ((id, listener) in persistentListeners) { - try { - val remover = persistentContextCache.getOrPut(id) { EventListenerContext(this, EventListenerId(id)) } - listener(remover) - } catch (e: Exception) { - if (e is InterruptedException) throw e - logger.error("Exception in listener", e) + if(catchExceptions) { + try { + run(id, listener) + } catch (e: Exception) { + if (e is InterruptedException) throw e + logger.error("Exception in listener", e) + } + } else { + run(id, listener) } } } @@ -112,11 +127,15 @@ class EventHandler(val name: String) : Runnable { val listener = toRunListeners.removeFirst() toRunIds.removeFirst() // keep in sync - try { + if(catchExceptions) { + try { + listener() + } catch (e: Exception) { + if (e is InterruptedException) throw e + logger.error("Exception in once listener", e) + } + } else { listener() - } catch (e: Exception) { - if (e is InterruptedException) throw e - logger.error("Exception in once listener", e) } } @@ -137,8 +156,19 @@ class EventHandler(val name: String) : Runnable { persistentAddQueue.addLast(id.value to listener) } - if (callRightAway) { - listener(EventListenerContext(this, id)) + + when(val mode = callRightAway) { + CallRightAway.InPlace -> listener(EventListenerContext(this, id)) + is CallRightAway.InScope -> { + val job = mode.scope.launch { + listener(EventListenerContext(this@EventHandler, id)) + } + + if(mode.shouldJoin) { + runBlocking { job.join() } + } + } + CallRightAway.Disabled -> {} // do nothing } return id @@ -147,12 +177,22 @@ class EventHandler(val name: String) : Runnable { fun once(listener: OnceEventListener): EventListenerId { val id = EventListenerId(idCounter.getAndIncrement()) - if (callRightAway) { - listener() - } else { - synchronized(onceLock) { - onceListenersQueue.addLast(listener) - onceIdsQueue.addLast(id.value) + when(val mode = callRightAway) { + CallRightAway.InPlace -> listener() + is CallRightAway.InScope -> { + val job = mode.scope.launch { + listener() + } + + if(mode.shouldJoin) { + runBlocking { job.join() } + } + } + CallRightAway.Disabled -> { + synchronized(onceLock) { + onceListenersQueue.addLast(listener) + onceIdsQueue.addLast(id.value) + } } } @@ -200,4 +240,10 @@ class EventHandler(val name: String) : Runnable { onceIdsQueue.clear() } } + + sealed interface CallRightAway { + data class InScope(val scope: CoroutineScope, val shouldJoin: Boolean = false) : CallRightAway + object InPlace : CallRightAway + object Disabled : CallRightAway + } } \ No newline at end of file diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/Orchestrator.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/Orchestrator.kt new file mode 100644 index 00000000..baa40792 --- /dev/null +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/Orchestrator.kt @@ -0,0 +1,183 @@ +@file:Suppress("unused") + +package com.github.serivesmejia.eocvsim.util.event + +import io.github.deltacv.common.util.loggerOf +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.runBlocking + +class Orchestrator( + val name: String? = null, + private val scope: CoroutineScope +) { + + private val logger by loggerOf("Orchestrator:${name ?: Integer.toHexString(hashCode())}") + + data class Registration( + val instance: Orchestrable, + val target: suspend () -> Unit, + val dependencies: List + ) + + class RegistrationCtx internal constructor( + val instance: T + ) { + + private val dependencyInstances = mutableListOf() + private var targetRunner: (suspend (T) -> Unit)? = null + + fun dependsOn(vararg targets: Orchestrable) { + for (target in targets) { + if (dependencyInstances.none { it === target }) { + dependencyInstances.add(target) + } + } + } + + fun target(block: suspend (T) -> Unit) { + targetRunner = block + } + + internal fun build(): Registration { + val runner = targetRunner + ?: throw IllegalArgumentException("register { } requires targetMethod { instance -> ... }") + + return Registration( + instance = instance, + target = { runner(instance) }, + dependencies = dependencyInstances.toList() + ) + } + } + + private val registrations = mutableListOf() + + fun register(targetInstance: T, block: RegistrationCtx.() -> Unit = {}) { + val registration = RegistrationCtx(targetInstance).apply(block).build() + register(registration) + } + + fun register(registration: Registration) { + if (registrations.any { it.instance === registration.instance }) { + throw IllegalArgumentException( + "Target instance already registered: ${registration.instance::class.qualifiedName}" + ) + } + + registrations.add(registration) + + logger.debug( + "Registered target ${registration.instance::class.qualifiedName} with ${registration.dependencies.size} dependency(ies)" + ) + } + + fun orchestrate() { + runBlocking(scope.coroutineContext) { + logger.info("Starting orchestration with ${registrations.size} registration(s)") + validateDependenciesExist() + + val pending = registrations.toMutableList() + val executedTargets = mutableListOf() + var wave = 0 + + while (pending.isNotEmpty()) { + wave++ + + val ready = pending.filter { registration -> + registration.dependencies.all { dep -> executedTargets.any { it === dep } } + } + + logger.debug( + "Wave $wave -> pending=${pending.size}, ready=${ready.size}, executed=${executedTargets.size}" + ) + + if (ready.isNotEmpty()) { + val readyNames = ready.joinToString { it.instance::class.qualifiedName ?: "" } + logger.debug("Wave $wave ready targets: $readyNames") + } + + if (ready.isEmpty()) { + val remaining = pending.joinToString { it.instance::class.qualifiedName ?: "" } + logger.error("Deadlock/cycle detected, remaining: $remaining") + + throw IllegalStateException( + "Circular dependency detected among remaining targets: $remaining" + ) + } + + // Execute this dependency wave in parallel. + runBatchInParallel(ready, wave) + + for (registration in ready) { + executedTargets.add(registration.instance) + pending.remove(registration) + } + } + + logger.info("Orchestration finished successfully") + } + } + + private suspend fun runBatchInParallel(registrations: List, wave: Int) = coroutineScope { + val start = System.nanoTime() + logger.debug("Wave $wave launching ${registrations.size} parallel target(s)") + + registrations.map { registration -> + async { + val targetName = registration.instance::class.qualifiedName ?: "" + val targetStart = System.nanoTime() + + logger.debug("Wave $wave start target $targetName") + invokeTarget(registration) + + val targetMs = (System.nanoTime() - targetStart) / 1_000_000 + logger.debug("Wave $wave finished target $targetName in ${targetMs}ms") + } + }.awaitAll() + + val batchMs = (System.nanoTime() - start) / 1_000_000 + logger.debug("Wave $wave completed in ${batchMs}ms") + } + + private fun validateDependenciesExist() { + logger.debug("Validating dependencies") + + for (registration in registrations) { + for (dependency in registration.dependencies) { + val exists = registrations.any { it.instance === dependency } + + if (!exists) { + logger.error( + "Unresolved dependency ${dependency::class.qualifiedName} required by ${registration.instance::class.qualifiedName}" + ) + + throw IllegalStateException( + "Unresolved dependency ${dependency::class.simpleName} required by ${registration.instance::class.simpleName}" + ) + } + } + } + + logger.debug("Dependency validation passed") + } + + private suspend fun invokeTarget(registration: Registration) { + try { + registration.target() + } catch (ex: Exception) { + val targetName = registration.instance::class.qualifiedName ?: "" + logger.error("Invocation failed for $targetName", ex) + + throw IllegalStateException( + "Invocation failed for $targetName", + ex + ) + } + } +} +interface Orchestrable { + +} diff --git a/EOCV-Sim/build.gradle b/EOCV-Sim/build.gradle index bb1bbc8d..d45fe2e3 100644 --- a/EOCV-Sim/build.gradle +++ b/EOCV-Sim/build.gradle @@ -1,154 +1,173 @@ -import io.github.fvarrui.javapackager.gradle.PackageTask - -import java.nio.file.Paths -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter - -plugins { - id 'org.jetbrains.kotlin.jvm' - id 'com.gradleup.shadow' - - id 'signing' - id "com.vanniktech.maven.publish" version "0.30.0" - - id 'io.github.fvarrui.javapackager.plugin' -} - -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() -} - -test { - useJUnitPlatform() -} - -apply from: '../test-logging.gradle' - -tasks.register('pack', PackageTask) { - dependsOn build - // mandatory - mainClass = 'com.github.serivesmejia.eocvsim.Main' - // optional - bundleJre = true - customizedJre = false - generateInstaller = true - platform = "auto" - - winConfig { - icoFile = file('src/main/resources/images/icon/ico_eocvsim.ico') - - generateMsi = false - disableDirPage = false - disableProgramGroupPage = false - disableFinishedPage = false - } - - linuxConfig { - pngFile = file('src/main/resources/images/icon/ico_eocvsim.png') - } -} - - -tasks.processResources { - from({ - project(":PaperVisionShadow").tasks.shadowJar - }) { - rename { "PaperVisionPlugin.jar" } // name inside resources - into("embedded_plugins") // folder inside resources - } -} - -dependencies { - api project(':Common') - api project(':Vision') - - implementation 'org.jetbrains.kotlin:kotlin-stdlib' - - implementation(platform("io.insert-koin:koin-bom:$koin_version")) - implementation("io.insert-koin:koin-core") - - implementation "org.eclipse.jdt:ecj:3.21.0" - - api "org.openpnp:opencv:$opencv_version" - - implementation "org.slf4j:slf4j-api:$slf4j_version" - implementation "org.apache.logging.log4j:log4j-api:$log4j_version" - implementation "org.apache.logging.log4j:log4j-core:$log4j_version" - implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$log4j_version" - - implementation "org.deltacv.steve:core:1.1.3" - implementation "org.deltacv.steve:backend-openpnp:1.1.3" - - implementation "info.picocli:picocli:$picocli_version" - implementation 'com.google.code.gson:gson:2.8.9' - implementation "io.github.classgraph:classgraph:$classgraph_version" - - implementation "com.formdev:flatlaf:$flatlaf_version" - implementation "com.formdev:flatlaf-intellij-themes:$flatlaf_version" - implementation "com.miglayout:miglayout-swing:$miglayout_version" - - implementation("org.java-websocket:Java-WebSocket:1.5.2") - - implementation 'net.lingala.zip4j:zip4j:2.11.3' - - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinx_coroutines_version" - implementation "org.jetbrains.kotlinx:kotlinx-coroutines-swing:$kotlinx_coroutines_version" - - testImplementation "io.kotest:kotest-runner-junit5:$kotest_version" - testImplementation "io.kotest:kotest-assertions-core:$kotest_version" - - 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' - implementation 'org.jboss.shrinkwrap.resolver:shrinkwrap-resolver-impl-maven-archive:3.3.2' - - implementation('org.deltacv.visionloop:streaming:1.2.9') { transitive = false } - - // implementation "org.deltacv.PaperVision:EOCVSimPlugin:$papervision_version" -} - -tasks.register('writeBuildClassJava') { - - String date = DateTimeFormatter.ofPattern("yyyy-M-d hh:mm:ss").format(LocalDateTime.now()) - - File versionFile = Paths.get( - projectDir.absolutePath, 'src', 'main', 'java', - 'com', 'github', 'serivesmejia', 'eocvsim', 'Build.java' - ).toFile() - - versionFile.delete() - - versionFile << "package com.github.serivesmejia.eocvsim;\n" + - "\n" + - "/*\n" + - " * Autogenerated file! Do not manually edit this file, as\n" + - " * it is regenerated any time the build task is run.\n" + - " *\n" + - " * Based from PhotonVision PhotonVersion generator task\n" + - " */\n" + - "@SuppressWarnings(\"ALL\")\n" + - "public final class Build {\n" + - " public static final String versionString = \"$version\";\n" + - " public static final String standardVersionString = \"$standardVersion\";\n" + - " public static final String buildDate = \"$date\";\n" + - " public static final boolean isDev = ${version.contains("dev")};\n\n" + - " public static final String opencvVersion = \"$opencv_version\";\n" + - " public static final String apriltagPluginVersion = \"$apriltag_plugin_version\";\n" + - " public static final String paperVisionVersion = \"$papervision_version\";\n" + - "}" -} - -build.dependsOn writeBuildClassJava \ No newline at end of file +import io.github.fvarrui.javapackager.gradle.PackageTask + +import java.nio.file.Paths +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + +plugins { + id 'org.jetbrains.kotlin.jvm' + id 'com.gradleup.shadow' + + id 'signing' + id "com.vanniktech.maven.publish" version "0.30.0" + + id 'io.github.fvarrui.javapackager.plugin' +} + +apply from: '../build.common.gradle' + +ext.kotest_version = '5.7.2' + +def generatedSourcesDir = layout.buildDirectory.dir("generated/sources/buildinfo/java/main") + +sourceSets { + main { + java { + srcDirs += generatedSourcesDir + } + } +} + +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() +} + +test { + useJUnitPlatform() +} + +apply from: '../test-logging.gradle' + +tasks.register('pack', PackageTask) { + dependsOn build + // mandatory + mainClass = 'com.github.serivesmejia.eocvsim.Main' + // optional + bundleJre = true + customizedJre = false + generateInstaller = true + platform = "auto" + + winConfig { + icoFile = file('src/main/resources/images/icon/ico_eocvsim.ico') + + generateMsi = false + disableDirPage = false + disableProgramGroupPage = false + disableFinishedPage = false + } + + linuxConfig { + pngFile = file('src/main/resources/images/icon/ico_eocvsim.png') + } +} + + +tasks.processResources { + from({ + project(":PaperVisionShadow").tasks.shadowJar + }) { + rename { "PaperVisionPlugin.jar" } // name inside resources + into("embedded_plugins") // folder inside resources + } +} + +dependencies { + api project(':Common') + api project(':Vision') + + implementation 'org.jetbrains.kotlin:kotlin-stdlib' + + implementation(platform("io.insert-koin:koin-bom:$koin_version")) + implementation("io.insert-koin:koin-core") + + implementation "org.eclipse.jdt:ecj:3.21.0" + + api "org.openpnp:opencv:$opencv_version" + + implementation "org.slf4j:slf4j-api:$slf4j_version" + implementation "org.apache.logging.log4j:log4j-api:$log4j_version" + implementation "org.apache.logging.log4j:log4j-core:$log4j_version" + implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$log4j_version" + + implementation "org.deltacv.steve:core:1.1.3" + implementation "org.deltacv.steve:backend-openpnp:1.1.3" + + implementation "info.picocli:picocli:$picocli_version" + implementation 'com.google.code.gson:gson:2.8.9' + implementation "io.github.classgraph:classgraph:$classgraph_version" + + implementation "com.formdev:flatlaf:$flatlaf_version" + implementation "com.formdev:flatlaf-intellij-themes:$flatlaf_version" + implementation "com.miglayout:miglayout-swing:$miglayout_version" + + implementation("org.java-websocket:Java-WebSocket:1.5.2") + + implementation 'net.lingala.zip4j:zip4j:2.11.3' + + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinx_coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-swing:$kotlinx_coroutines_version" + + testImplementation "io.kotest:kotest-runner-junit5:$kotest_version" + testImplementation "io.kotest:kotest-assertions-core:$kotest_version" + + 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' + implementation 'org.jboss.shrinkwrap.resolver:shrinkwrap-resolver-impl-maven-archive:3.3.2' + + implementation('org.deltacv.visionloop:streaming:1.2.9') { transitive = false } + + // implementation "org.deltacv.PaperVision:EOCVSimPlugin:$papervision_version" +} + +tasks.register('writeBuildClassJava') { + outputs.dir(generatedSourcesDir) + + doLast { + String date = DateTimeFormatter.ofPattern("yyyy-M-d hh:mm:ss").format(LocalDateTime.now()) + + def packageStr = "com.github.serivesmejia.eocvsim" + def packagePath = packageStr.replace('.', '/') + + def outputDir = file("${generatedSourcesDir.get().asFile}/$packagePath") + outputDir.mkdirs() + + def versionFile = file("${outputDir}/Build.java") + + versionFile.text = """package $packageStr; + +/* + * Autogenerated file! Do not manually edit this file, as + * it is regenerated any time the build task is run. + * + * Based from PhotonVision PhotonVersion generator task + */ +@SuppressWarnings("ALL") +public final class Build { + public static final String versionString = "$version"; + public static final String standardVersionString = "$standardVersion"; + public static final String buildDate = "$date"; + public static final boolean isDev = ${version.contains("dev")}; + + public static final String opencvVersion = "$opencv_version"; + public static final String apriltagPluginVersion = "$apriltag_plugin_version"; + public static final String paperVisionVersion = "$papervision_version"; +} +""" + } +} + +compileJava.dependsOn writeBuildClassJava +if (tasks.findByName("compileKotlin")) { + compileKotlin.dependsOn writeBuildClassJava +} \ No newline at end of file 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 52990cc8..b406feab 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 @@ -27,26 +27,19 @@ import com.github.serivesmejia.eocvsim.config.Config import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.Visualizer -import com.github.serivesmejia.eocvsim.gui.dialog.FileAlreadyExists import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.github.serivesmejia.eocvsim.output.RecordingManager -import com.github.serivesmejia.eocvsim.output.VideoRecordingSession - import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.pipeline.PipelineSource -import com.github.serivesmejia.eocvsim.pipeline.compiler.CompiledPipelineManager import com.github.serivesmejia.eocvsim.tuner.TunerManager -import com.github.serivesmejia.eocvsim.util.ClasspathScan -import com.github.serivesmejia.eocvsim.util.FileFilters import com.github.serivesmejia.eocvsim.util.JavaProcess -import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.util.event.Orchestrator import com.github.serivesmejia.eocvsim.util.exception.handling.CrashReport import com.github.serivesmejia.eocvsim.util.exception.handling.EOCVSimUncaughtExceptionHandler import com.github.serivesmejia.eocvsim.util.fps.FpsLimiter import com.github.serivesmejia.eocvsim.util.io.EOCVSimFolder import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager -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 @@ -56,20 +49,17 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel +import kotlinx.coroutines.channels.Channel import nu.pattern.OpenCV import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.koin.core.qualifier.named import org.opencv.core.Mat import org.opencv.core.Size -import org.openftc.easyopencv.OpenCvViewport import org.openftc.easyopencv.TimestampedPipelineHandler import java.io.File import java.lang.Thread.sleep import javax.swing.JOptionPane -import javax.swing.SwingUtilities -import javax.swing.filechooser.FileFilter -import javax.swing.filechooser.FileNameExtensionFilter import kotlin.system.exitProcess /** @@ -77,10 +67,9 @@ import kotlin.system.exitProcess * This class is the entry point of the program * and is responsible for initializing all the * components of the simulator. - * @param params the parameters to initialize the simulator with * @see Parameters */ -class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { +class EOCVSim : KoinComponent { companion object { const val VERSION = Build.versionString @@ -94,7 +83,6 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { @JvmField val DEFAULT_EOCV_SIZE = Size(DEFAULT_EOCV_WIDTH.toDouble(), DEFAULT_EOCV_HEIGHT.toDouble()) - val logger by loggerFor(EOCVSim::class) init { @@ -116,7 +104,7 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { try { System.load(alternativeNative.absolutePath) - Mat().release() //test if native lib is loaded correctly + Mat().release() //test if OpenCV is loaded correctly isNativeLibLoaded = true logger.info("Successfully loaded the OpenCV native lib from specified path") @@ -133,7 +121,7 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { logger.info("Successfully loaded the OpenCV native lib") } catch (ex: Throwable) { logger.error("Failure loading the OpenCV native lib", ex) - logger.error("The sim will exit now as it's impossible to continue execution without OpenCV") + logger.error("The sim will exit now as it's impossible to continue without OpenCV") CrashReport(ex).saveCrashReport() @@ -144,6 +132,10 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { } } + val parameters: Parameters by inject() + + val initOrchestrator: Orchestrator by inject(named("init")) + /** * Event handler for the main update loop * This event handler is called every frame @@ -151,10 +143,7 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { * posted by the different components of the simulator * @see EventHandler */ - val onMainUpdate: EventHandler by inject(named("onMainLoop")) - val onRestartRequested: EventHandler by inject(named("onRestartRequested")) - val onDestroyRequested: EventHandler by inject(named("onDestroyRequested")) - + val onMainLoop: EventHandler by inject(named("onMainLoop")) /** * The visualizer instance in charge of managing the GUI @@ -170,7 +159,6 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { val recordingManager: RecordingManager by inject() val dialogFactory: DialogFactory by inject() - /** * The pipeline statistics calculator instance in charge of * calculating the average FPS, pipeline time and overhead time @@ -204,7 +192,7 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { */ val config: Config get() = configManager.config - val classpathScan: ClasspathScan by inject() + val lifecycleChannel: Channel by inject(named("lifecycle")) /** * Utility in charge of limiting the FPS of the simulator @@ -222,37 +210,15 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { private var isRestarting = false private var destroying = false - /** - * The reason why the simulator was destroyed - * to handle different actions when flushing - * the simulator away. - * @see destroy - */ - enum class DestroyReason { - USER_REQUESTED, THREAD_EXIT, RESTART, CRASH - } - - private val pipelineRenderHook = - OpenCvViewport.RenderHook { - canvas, onscreenWidth, onscreenHeight, scaleBmpPxToCanvasPx, scaleCanvasDensity, userContext -> - if (pipelineManager.hasInitCurrentPipeline) { - pipelineManager.currentPipeline?.onDrawFrame(canvas, onscreenWidth, onscreenHeight, scaleBmpPxToCanvasPx, scaleCanvasDensity, userContext) - } - } - /** * Initializes the simulator * This method is called to initialize all the components * of the simulator and start the main loop - * @see start + * @see mainLoop */ - fun init() { + fun start() { eocvSimThread = Thread.currentThread() - // Wire up lifecycle events so components can trigger restart/destroy without injecting EOCVSim - onRestartRequested { restart() } - onDestroyRequested { destroy() } - if (!EOCVSimFolder.couldLock) { logger.error( "Couldn't finally claim lock file in \"${EOCVSimFolder.absolutePath}\"! " + "Is the folder opened by another EOCV-Sim instance?" @@ -278,18 +244,9 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { EOCVSimUncaughtExceptionHandler.register() //loading native lib only once in the app runtime - loadOpenCvLib(params.opencvNativeLibrary) - - classpathScan.asyncScan(scope) - - configManager.init() - - configManager.config.simTheme.install() + loadOpenCvLib(parameters.opencvNativeLibrary) - pluginManager.init() // woah - pluginManager.loadPlugins() - - workspaceManager.init() + initOrchestrator.orchestrate() visualizer.onInitFinished { // SHOW WELCOME DIALOGS TO NEW USERS @@ -312,24 +269,6 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { // END OF WELCOME DIALOGS } - visualizer.initAsync(configManager.config.simTheme) //create gui in the EDT - - inputSourceManager.init() //loading user created input sources - - pipelineManager.init() //init pipeline manager (scan for pipelines) - - tunerManager.init() //init tunable variables manager - - //shows a warning when a pipeline gets "stuck" - pipelineManager.onPipelineTimeout { - dialogFactory.createInformation( - visualizer.frame, - "Current pipeline took too long to ${pipelineManager.lastPipelineAction}", - "Falling back to DefaultPipeline", - "Operation failed" - ) - } - inputSourceManager.inputSourceLoader.saveInputSourcesToFile() visualizer.joinInit() @@ -353,48 +292,21 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { //post output mats from the pipeline to the visualizer viewport pipelineManager.pipelineOutputPosters.add(visualizer.viewport) - // now that we have two different runnable units (OpenCvPipeline and OpMode) - // we have to give a more special treatment to the OpenCvPipeline - // OpModes can take care of themselves, setting up their own stuff - // but we need to do some hand holding for OpenCvPipelines... - pipelineManager.onPipelineChange { - pipelineStatisticsCalculator.init() - - if(pipelineManager.currentPipeline !is OpMode && pipelineManager.currentPipeline != null) { - visualizer.viewport.activate() - visualizer.viewport.setRenderHook(pipelineRenderHook) // calls OpenCvPipeline#onDrawFrame on the viewport (UI) thread - } else { - // opmodes are on their own, lol - visualizer.viewport.deactivate() - visualizer.viewport.clearViewport() - } - } - - pipelineManager.onUpdate { - if(pipelineManager.currentPipeline !is OpMode && pipelineManager.currentPipeline != null) { - visualizer.viewport.notifyStatistics( - pipelineStatisticsCalculator.avgFps, - pipelineStatisticsCalculator.avgPipelineTime, - pipelineStatisticsCalculator.avgOverheadTime - ) - } - - updateVisualizerTitle() // update current pipeline in title - } - pluginManager.enablePlugins() + // BEGIN + try { - start() + mainLoop() } catch (e: InterruptedException) { logger.warn("Main thread interrupted ($hexCode)", e) } - if(!destroying) { - destroy(DestroyReason.THREAD_EXIT) - } + // HANDLE MAIN LOOP EXITS - if (isRestarting) { + if(!destroying) { + destroy(LifecycleSignal.Destroy.Reason.THREAD_EXIT) + } else if (isRestarting) { Thread.interrupted() //clear interrupted flag EOCVSimFolder.lock?.lock?.close() @@ -419,10 +331,10 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { * such as the GUI, the pipelines, the input sources, * and the different manages declared at the top level * are explicitly updated within this method - * @see init + * @see start */ @Throws(InterruptedException::class) - private fun start() { + private fun mainLoop() { if(Thread.currentThread() != eocvSimThread) { throw IllegalStateException("start() must be called from the EOCVSim thread") } @@ -431,7 +343,7 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { while (!eocvSimThread.isInterrupted && !destroying) { //run all pending requested runnables - onMainUpdate.run() + onMainLoop.run() pipelineStatisticsCalculator.newInputFrameStart() @@ -448,6 +360,18 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { } catch (_: InterruptedException) { break } + + when(val signal = lifecycleChannel.tryReceive().getOrNull()) { + is LifecycleSignal.Restart -> { + logger.info("Restart signal received") + restart() + } + is LifecycleSignal.Destroy -> { + logger.info("Destroy signal received") + destroy(signal.reason) + } + null -> {} + } } logger.warn("Main thread interrupted ($hexCode)") @@ -457,7 +381,7 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { * Destroys the simulator * @param reason the reason why the simulator is being destroyed, it mainly allows to restart the simulator if requested */ - fun destroy(reason: DestroyReason) { + fun destroy(reason: LifecycleSignal.Destroy.Reason) { logger.warn("-- Destroying current EOCVSim ($hexCode) due to $reason, it is normal to see InterruptedExceptions and other kinds of stack traces below --") pluginManager.disablePlugins() @@ -465,7 +389,6 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { //stop recording session if there's currently an ongoing one recordingManager.stopRecordingSession() - logger.info("Trying to save config file...") inputSourceManager.currentInputSource?.close() @@ -476,20 +399,21 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { destroying = true scope.cancel() - if(reason == DestroyReason.THREAD_EXIT) { + if(reason == LifecycleSignal.Destroy.Reason.THREAD_EXIT) { exitProcess(0) } else { eocvSimThread.interrupt() } - if (reason == DestroyReason.USER_REQUESTED || reason == DestroyReason.CRASH) jvmMainThread.interrupt() + if (reason == LifecycleSignal.Destroy.Reason.USER_REQUESTED || reason == LifecycleSignal.Destroy.Reason.CRASH) + jvmMainThread.interrupt() } /** * Destroys the simulator with the reason being USER_REQUESTED */ fun destroy() { - destroy(DestroyReason.USER_REQUESTED) + destroy(LifecycleSignal.Destroy.Reason.USER_REQUESTED) } /** @@ -502,41 +426,15 @@ class EOCVSim(val params: Parameters = Parameters()) : KoinComponent { pipelineManager.captureStaticSnapshot() isRestarting = true - destroy(DestroyReason.RESTART) + destroy(LifecycleSignal.Destroy.Reason.USER_REQUESTED) } - - /** * Checks if the simulator is currently recording * @return true if the simulator is currently recording, false otherwise */ fun isCurrentlyRecording() = recordingManager.isCurrentlyRecording() - - /** - * Updates the visualizer title message - * with different information such as the current pipeline - * the workspace file, if the pipeline is paused, if the pipeline - * is currently building, and if the simulator is currently recording - */ - private fun updateVisualizerTitle() { - val isBuildRunning = if (pipelineManager.compiledPipelineManager.isBuildRunning) "(Building)" else "" - - val workspaceMsg = " - ${workspaceManager.workspaceFile.absolutePath} $isBuildRunning" - - val isPaused = if (pipelineManager.paused) " (Paused)" else "" - val isRecording = if (isCurrentlyRecording()) " RECORDING" else "" - - val msg = isRecording + isPaused - - if (pipelineManager.currentPipeline == null) { - visualizer.setTitleMessage("No pipeline$msg${workspaceMsg}") - } else { - visualizer.setTitleMessage("${pipelineManager.currentPipelineName}$msg${workspaceMsg}") - } - } - /** * Parameters class to initialize the simulator with * @see EOCVSim diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Lifecycle.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Lifecycle.kt new file mode 100644 index 00000000..0a2b3c59 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Lifecycle.kt @@ -0,0 +1,8 @@ +package com.github.serivesmejia.eocvsim + +sealed interface LifecycleSignal { + class Destroy(val reason: Reason) : LifecycleSignal { + enum class Reason { USER_REQUESTED, THREAD_EXIT, CRASH } + } + object Restart : LifecycleSignal +} \ No newline at end of file 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 e0c619c9..9780a463 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 @@ -23,10 +23,8 @@ package com.github.serivesmejia.eocvsim -import com.github.serivesmejia.eocvsim.di.makeEOCVSimModule import com.github.serivesmejia.eocvsim.pipeline.PipelineSource import org.koin.core.context.GlobalContext -import org.koin.core.component.KoinComponent import org.koin.dsl.module import picocli.CommandLine import java.io.File @@ -113,13 +111,17 @@ class EOCVSimCommandInterface : Runnable { } GlobalContext.startKoin { - modules(makeEOCVSimModule(), module { single { parameters } }) + modules( + eocvSimModule, + module { + single { EOCVSim() } + single { parameters } + } + ) } - val eocvSim = EOCVSim(parameters) - GlobalContext.loadKoinModules(module { single { eocvSim } }) - - eocvSim.init() + // get eocv-sim from DI and start + GlobalContext.get().get().start() } private fun checkPath(parameter: String, path: String, shouldBeDirectory: Boolean): File { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/di/EOCVSimModule.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt similarity index 50% rename from EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/di/EOCVSimModule.kt rename to EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt index b0ea9f3a..71c8993d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/di/EOCVSimModule.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt @@ -1,69 +1,50 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.di - -import com.github.serivesmejia.eocvsim.gui.Visualizer -import com.github.serivesmejia.eocvsim.input.InputSourceManager -import com.github.serivesmejia.eocvsim.pipeline.PipelineManager -import com.github.serivesmejia.eocvsim.tuner.TunerManager -import com.github.serivesmejia.eocvsim.config.ConfigManager -import com.github.serivesmejia.eocvsim.input.InputSourceInitializer -import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager -import com.github.serivesmejia.eocvsim.pipeline.compiler.CompiledPipelineManager -import com.github.serivesmejia.eocvsim.util.ClasspathScan -import com.github.serivesmejia.eocvsim.util.event.EventHandler -import com.github.serivesmejia.eocvsim.gui.DialogFactory -import io.github.deltacv.eocvsim.plugin.loader.PluginManager - -import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob -import org.koin.core.qualifier.named -import org.koin.dsl.module - -fun makeEOCVSimModule() = module { - single { PipelineStatisticsCalculator() } - single { ConfigManager() } - single { ClasspathScan() } - - // global scope for launching coroutines within the app - single { CoroutineScope(SupervisorJob() + Dispatchers.Default) } - - single { WorkspaceManager() } - single { PipelineManager() } - single { InputSourceManager() } - single { InputSourceInitializer() } - single { TunerManager() } - single { PluginManager() } - single { CompiledPipelineManager() } - single { Visualizer() } - single { DialogFactory() } - single { com.github.serivesmejia.eocvsim.output.RecordingManager() } - - - single(named("onMainLoop")) { EventHandler("MainLoop") } - single(named("onRestartRequested")) { EventHandler("RestartRequested") } - single(named("onDestroyRequested")) { EventHandler("DestroyRequested") } +package com.github.serivesmejia.eocvsim + +import com.github.serivesmejia.eocvsim.config.ConfigManager +import com.github.serivesmejia.eocvsim.gui.DialogFactory +import com.github.serivesmejia.eocvsim.gui.Visualizer +import com.github.serivesmejia.eocvsim.input.InputSourceInitializer +import com.github.serivesmejia.eocvsim.input.InputSourceManager +import com.github.serivesmejia.eocvsim.output.RecordingManager +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import com.github.serivesmejia.eocvsim.pipeline.compiler.CompiledPipelineManager +import com.github.serivesmejia.eocvsim.tuner.TunerManager +import com.github.serivesmejia.eocvsim.util.AutoClasspathScan +import com.github.serivesmejia.eocvsim.util.ClasspathScan +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.util.event.Orchestrator +import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager +import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator +import io.github.deltacv.eocvsim.plugin.loader.PluginManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.channels.Channel +import org.koin.core.qualifier.named +import org.koin.dsl.module + +val eocvSimModule = module { + single { PipelineStatisticsCalculator() } + single { AutoClasspathScan() } + + // global scope for launching coroutines within the app + single { CoroutineScope(SupervisorJob() + Dispatchers.Default) } + + single { ConfigManager() } + single { WorkspaceManager() } + single { PipelineManager() } + single { InputSourceManager() } + single { InputSourceInitializer() } + single { TunerManager() } + single { PluginManager() } + single { CompiledPipelineManager() } + + single { Visualizer() } + single { DialogFactory() } + + single { RecordingManager() } + + single(named("init")) { Orchestrator("Init", scope = get()) } + single(named("lifecycle")) { Channel(Channel.BUFFERED) } + single(named("onMainLoop")) { EventHandler("OnMainLoop") } } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt index c4e3a370..a968f324 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt @@ -23,17 +23,32 @@ package com.github.serivesmejia.eocvsim.config +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.util.event.Orchestrable +import com.github.serivesmejia.eocvsim.util.event.Orchestrator import io.github.deltacv.common.util.loggerForThis +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named -class ConfigManager { +class ConfigManager : Orchestrable, KoinComponent { val configLoader = ConfigLoader() + var config: Config = Config() private set private val logger by loggerForThis() - fun init() { + private val initOrchestrator: Orchestrator by inject(named("init")) + + init { + initOrchestrator.register(this) { + target { it.init() } + } + } + + private fun init() { logger.info("Initializing...") try { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt index 5a7b4725..560c8b29 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt @@ -23,10 +23,8 @@ package com.github.serivesmejia.eocvsim.gui -import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.gui.dialog.* -import com.github.serivesmejia.eocvsim.gui.dialog.SplashScreen // Explicit import to resolve reference 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.* @@ -34,23 +32,20 @@ import com.github.serivesmejia.eocvsim.input.SourceType import com.github.serivesmejia.eocvsim.util.event.EventHandler import io.github.deltacv.eocvsim.plugin.loader.PluginManager import kotlinx.coroutines.CoroutineScope -import java.awt.* +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import java.awt.Component +import java.awt.GridLayout import java.io.File -import java.util.* import javax.swing.* import javax.swing.filechooser.FileFilter import javax.swing.filechooser.FileNameExtensionFilter -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject -import org.koin.core.qualifier.named - class DialogFactory : KoinComponent { val visualizer: Visualizer by inject() val pluginManager: PluginManager by inject() val configManager: ConfigManager by inject() - val onRestartRequested: EventHandler by inject(named("onRestartRequested")) val scope: CoroutineScope by inject() fun createYesOrNo(parent: Component?, message: String, submessage: String, result: (Int) -> Unit) { @@ -181,7 +176,7 @@ class DialogFactory : KoinComponent { fun createMavenOutput(onContinue: Runnable?): AppendDelegate { val delegate = AppendDelegate() - invokeLater { PluginOutput(delegate, pluginManager, onRestartRequested, configManager, scope, onContinue ?: Runnable { }) } + invokeLater { PluginOutput(delegate, pluginManager, configManager, scope, onContinue ?: Runnable { }) } return delegate } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt index 7b855829..15554c9d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt @@ -25,7 +25,8 @@ package com.github.serivesmejia.eocvsim.gui import com.formdev.flatlaf.FlatLaf import com.github.serivesmejia.eocvsim.Build -import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.LifecycleSignal +import com.github.serivesmejia.eocvsim.LifecycleSignal.Destroy.Reason import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.gui.component.CollapsiblePanelX import com.github.serivesmejia.eocvsim.gui.component.tuner.ColorPicker @@ -57,22 +58,38 @@ import java.awt.event.WindowEvent import javax.swing.* import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import com.qualcomm.robotcore.eventloop.opmode.OpMode +import com.github.serivesmejia.eocvsim.output.RecordingManager +import com.github.serivesmejia.eocvsim.util.event.Orchestrable +import com.github.serivesmejia.eocvsim.util.event.Orchestrator +import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator +import org.openftc.easyopencv.OpenCvViewport import org.koin.core.qualifier.named import org.koin.core.component.KoinComponent import org.koin.core.component.inject import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.swing.Swing +import kotlinx.coroutines.withContext -class Visualizer : KoinComponent { +class Visualizer : Orchestrable, KoinComponent { val onMainUpdate: EventHandler by inject(named("onMainLoop")) - val onDestroyRequested: EventHandler by inject(named("onDestroyRequested")) + + val lifecycleChannel: Channel by inject(named("lifecycle")) + val pipelineManager: PipelineManager by inject() val inputSourceManager: InputSourceManager by inject() val configManager: ConfigManager by inject() val workspaceManager: WorkspaceManager by inject() val dialogFactory: DialogFactory by inject() + val recordingManager: RecordingManager by inject() + val pipelineStatisticsCalculator: PipelineStatisticsCalculator by inject() val scope: CoroutineScope by inject() + private val initOrchestrator: Orchestrator by inject(named("init")) + val onInitFinished = EventHandler("OnVisualizerInitFinish") val onPluginGuiAttachment = EventHandler("OnPluginGuiAttachment") @@ -127,7 +144,26 @@ class Visualizer : KoinComponent { private val logger by loggerForThis() - fun init(theme: Theme) { + private val pipelineRenderHook = + OpenCvViewport.RenderHook { + canvas, onscreenWidth, onscreenHeight, scaleBmpPxToCanvasPx, scaleCanvasDensity, userContext -> + if (pipelineManager.hasInitCurrentPipeline) { + pipelineManager.currentPipeline?.onDrawFrame(canvas, onscreenWidth, onscreenHeight, scaleBmpPxToCanvasPx, scaleCanvasDensity, userContext) + } + } + + init { + initOrchestrator.register(this) { + target { + withContext(Dispatchers.Swing) { + it.init(configManager.config.simTheme) + } + } + dependsOn(configManager) + } + } + + private fun init(theme: Theme) { try { theme.install() } catch (e: Exception) { @@ -190,7 +226,7 @@ class Visualizer : KoinComponent { tunerCollapsible.contentPanel.add(tunerScrollPane) onPluginGuiAttachment.run() - onPluginGuiAttachment.callRightAway = true + onPluginGuiAttachment.callRightAway = EventHandler.CallRightAway.InPlace frame.add(tunerCollapsible, BorderLayout.SOUTH) frame.add(sidebarContainer, BorderLayout.EAST) @@ -221,12 +257,11 @@ class Visualizer : KoinComponent { frame.setLocationRelativeTo(null) frame.extendedState = JFrame.MAXIMIZED_BOTH - frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE frame.isVisible = true onInitFinished.run() - onInitFinished.callRightAway = true + onInitFinished.callRightAway = EventHandler.CallRightAway.InPlace registerListeners() @@ -243,17 +278,14 @@ class Visualizer : KoinComponent { if (!PipelineCompiler.IS_USABLE) { compilerUnsupported() } - } - fun initAsync(simTheme: Theme) { - SwingUtilities.invokeLater { init(simTheme) } + setupSubscriptions() } private fun registerListeners() { frame.addWindowListener(object : WindowAdapter() { override fun windowClosing(e: WindowEvent) { - onMainUpdate.once { onDestroyRequested.run() } - + lifecycleChannel.trySend(LifecycleSignal.Destroy(Reason.USER_REQUESTED)) } }) @@ -277,7 +309,7 @@ class Visualizer : KoinComponent { fun close() { SwingUtilities.invokeLater { frame.isVisible = false - viewport.deactivate() + viewport.dispose() frame.dispose() } } @@ -298,6 +330,59 @@ class Visualizer : KoinComponent { beforeTitleMsg = titleMsg } + private fun updateTitle() { + val isBuildRunning = if (pipelineManager.compiledPipelineManager.isBuildRunning) "(Building)" else "" + + val workspaceMsg = " - ${workspaceManager.workspaceFile.absolutePath} $isBuildRunning" + + val isPaused = if (pipelineManager.paused) " (Paused)" else "" + val isRecording = if (recordingManager.isCurrentlyRecording()) " RECORDING" else "" + + val msg = isRecording + isPaused + + if (pipelineManager.currentPipeline == null) { + setTitleMessage("No pipeline$msg${workspaceMsg}") + } else { + setTitleMessage("${pipelineManager.currentPipelineName}$msg${workspaceMsg}") + } + } + + private fun setupSubscriptions() { + pipelineManager.onPipelineChange { + colorPicker.stopPicking() + pipelineStatisticsCalculator.init() + + if(pipelineManager.currentPipeline !is OpMode && pipelineManager.currentPipeline != null) { + viewport.activate() + viewport.setRenderHook(pipelineRenderHook) + } else { + viewport.deactivate() + viewport.clearViewport() + } + } + + pipelineManager.onUpdate { + if(pipelineManager.currentPipeline !is OpMode && pipelineManager.currentPipeline != null) { + viewport.notifyStatistics( + pipelineStatisticsCalculator.avgFps, + pipelineStatisticsCalculator.avgPipelineTime, + pipelineStatisticsCalculator.avgOverheadTime + ) + } + + updateTitle() + } + + pipelineManager.onPipelineTimeout { + dialogFactory.createInformation( + frame, + "Current pipeline took too long to ${pipelineManager.lastPipelineAction}", + "Falling back to DefaultPipeline", + "Operation failed" + ) + } + } + fun updateTunerFields(fields: List) { tunerMenuPanel.removeAll() tunerMenuPanel.repaint() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/SidebarPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/SidebarPanel.kt index b7f53829..bbe04870 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/SidebarPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/SidebarPanel.kt @@ -32,14 +32,10 @@ import javax.swing.JPanel import javax.swing.JTabbedPane import org.koin.core.component.KoinComponent -import org.koin.core.component.inject class SidebarPanel : JTabbedPane(), KoinComponent { - - - - private var previousActiveIndex = -1; + private var previousActiveIndex = -1 val onTabChange = EventHandler("SidebarPanel-OnTabChange") @@ -63,9 +59,12 @@ class SidebarPanel : JTabbedPane(), KoinComponent { addChangeListener { val index = this.selectedIndex + val currentIndexValid = index in 0 until componentCount + val previousIndexValid = previousActiveIndex in 0 until componentCount // deactivate previous pane first - val previousPane = if (previousActiveIndex != -1) this.getComponentAt(previousActiveIndex) else null + val previousPane = if (previousIndexValid) this.getComponentAt(previousActiveIndex) else null + if (previousPane is TabJPanel && previousActiveIndex != index) { previousPane.isActive = false @@ -74,7 +73,7 @@ class SidebarPanel : JTabbedPane(), KoinComponent { } // activate current pane - val currentPane = this.getComponentAt(index) + val currentPane = if (currentIndexValid) this.getComponentAt(index) else null if (currentPane is TabJPanel && previousActiveIndex != index) { currentPane.isActive = true @@ -82,7 +81,7 @@ class SidebarPanel : JTabbedPane(), KoinComponent { logger.info("Activating $name") } - previousActiveIndex = index + previousActiveIndex = if (currentIndexValid) index else -1 onTabChange.run() } 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 7303d128..61918eb8 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 @@ -24,6 +24,7 @@ package com.github.serivesmejia.eocvsim.gui.component.visualizer import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.LifecycleSignal import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.gui.dialog.Output @@ -50,6 +51,7 @@ import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.util.event.EventHandler import org.koin.core.qualifier.named import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.Channel import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -61,13 +63,9 @@ class TopMenuBar : JMenuBar(), KoinComponent { val workspaceManager: WorkspaceManager by inject() val pipelineManager: PipelineManager by inject() val onMainUpdate: EventHandler by inject(named("onMainLoop")) - val onRestartRequested: EventHandler by inject(named("onRestartRequested")) + val lifecycleChannel: Channel by inject(named("lifecycle")) val scope: CoroutineScope by inject() - - - - companion object { val docsUrl = URI("https://docs.deltacv.org/eocv-sim/") } @@ -137,7 +135,7 @@ class TopMenuBar : JMenuBar(), KoinComponent { val fileRestart = JMenuItem("Restart") - fileRestart.addActionListener { onMainUpdate.once { onRestartRequested.run() } } + fileRestart.addActionListener { onMainUpdate.once { lifecycleChannel.trySend(LifecycleSignal.Restart) } } mFileMenu.add(fileRestart) 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 9b1f6dd6..1c02559b 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 @@ -23,6 +23,7 @@ package com.github.serivesmejia.eocvsim.gui.dialog +import com.github.serivesmejia.eocvsim.LifecycleSignal import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.gui.dialog.component.BottomButtonsPanel import com.github.serivesmejia.eocvsim.gui.dialog.component.OutputPanel @@ -31,9 +32,12 @@ import io.github.deltacv.eocvsim.plugin.loader.FilePluginLoaderImpl 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 com.github.serivesmejia.eocvsim.util.event.EventHandler import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.swing.Swing +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named import java.awt.Dimension import java.awt.GridBagConstraints import java.awt.GridBagLayout @@ -47,11 +51,10 @@ import javax.swing.event.ChangeListener class PluginOutput( appendDelegate: AppendDelegate, val pluginManager: PluginManager, - val onRestartRequested: EventHandler, val configManager: ConfigManager, val scope: CoroutineScope, val onContinue: Runnable -) : Appendable { +) : Appendable, KoinComponent { companion object { const val SPECIAL = "[13mck]" @@ -74,6 +77,8 @@ class PluginOutput( } } + private val lifecycleChannel: Channel by inject(named("lifecycle")) + private val output = JDialog() private val tabbedPane: JTabbedPane @@ -130,14 +135,11 @@ class PluginOutput( mavenBottomButtonsPanel.closeButton.isEnabled = true } - tabbedPane.addChangeListener(object: ChangeListener { - override fun stateChanged(e: ChangeEvent?) { - // remake plugin manager panel - if(tabbedPane.selectedIndex == tabbedPane.indexOfTab("Plugins")) { - tabbedPane.setComponentAt(0, makePluginManagerPanel()) - } + tabbedPane.addChangeListener { // remake plugin manager panel + if (tabbedPane.selectedIndex == tabbedPane.indexOfTab("Plugins")) { + tabbedPane.setComponentAt(0, makePluginManagerPanel()) } - }) + } } private fun checkShouldAskForRestart() { @@ -151,7 +153,7 @@ class PluginOutput( if(dialogResult == JOptionPane.YES_OPTION) { output.isVisible = false - onRestartRequested.run() + lifecycleChannel.trySend(LifecycleSignal.Restart) } shouldAskForRestart = false diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt index 52726549..f78b8c18 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt @@ -29,7 +29,6 @@ class InputSourceInitializer : KoinComponent { } } - private val inputSourceManager: InputSourceManager by inject() private val scope: CoroutineScope by inject() val logger by loggerForThis() @@ -46,8 +45,6 @@ class InputSourceInitializer : KoinComponent { } } - val dialog = inputSourceManager.showLoadingDialogIfNeeded(inputSource.name, job) - runBlocking { try { withTimeout(TIMEOUT) { @@ -57,14 +54,12 @@ class InputSourceInitializer : KoinComponent { logger.error("InputSource initialization timed out after $TIMEOUT ms", e) } finally { job.cancel() - dialog?.dispose() } } return result } - @OptIn(DelicateCoroutinesApi::class) @JvmOverloads fun runWithTimeout(sourceName: String, manager: InputSourceManager? = null, callback: () -> Boolean): Boolean { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt index 1e32c1bc..4f03fd63 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt @@ -31,8 +31,9 @@ import com.github.serivesmejia.eocvsim.input.source.NullSource import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.util.event.Orchestrable +import com.github.serivesmejia.eocvsim.util.event.Orchestrator import io.github.deltacv.common.util.loggerForThis -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -46,12 +47,14 @@ import java.io.IOException import java.util.concurrent.CancellationException import javax.swing.JDialog -class InputSourceManager : KoinComponent { +class InputSourceManager : Orchestrable, KoinComponent { private val pipelineManager: PipelineManager by inject() private val configManager: ConfigManager by inject() private val visualizer: Visualizer by inject() private val dialogFactory: DialogFactory by inject() + + private val initOrchestrator: Orchestrator by inject(named("init")) private val onMainLoop: EventHandler by inject(named("onMainLoop")) private val inputSourceInitializer: InputSourceInitializer by inject() @@ -80,7 +83,13 @@ class InputSourceManager : KoinComponent { private val logger by loggerForThis() - fun init() { + init { + initOrchestrator.register(this) { + target { it.init() } + } + } + + private fun init() { logger.info("Initializing...") matRecycler = MatRecycler(4) @@ -119,7 +128,7 @@ class InputSourceManager : KoinComponent { private fun createDefaultImgInputSource(resourcePath: String, fileName: String, sourceName: String, imgSize: Size) { try { val `is` = InputSource::class.java.getResourceAsStream(resourcePath) - val f = SysUtil.copyFileIsTemp(`is`, fileName, true).file + val f = SysUtil.copyFileIsTemp(`is`, fileName, false).file val src = ImageSource(f.absolutePath, imgSize).apply { isDefault = true diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt index 97ded9bc..7a0e6523 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt @@ -23,6 +23,7 @@ package com.github.serivesmejia.eocvsim.pipeline +import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.Visualizer @@ -40,11 +41,12 @@ import com.github.serivesmejia.eocvsim.util.ReflectUtil import com.github.serivesmejia.eocvsim.util.StrUtil import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.util.event.Orchestrable +import com.github.serivesmejia.eocvsim.util.event.Orchestrator import com.github.serivesmejia.eocvsim.util.fps.FpsCounter -import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager -import io.github.deltacv.common.util.loggerForThis import io.github.deltacv.common.image.MatPoster import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator +import io.github.deltacv.common.util.loggerForThis import io.github.deltacv.eocvsim.virtualreflect.VirtualField import io.github.deltacv.eocvsim.virtualreflect.VirtualReflection import io.github.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflection @@ -55,28 +57,28 @@ import org.firstinspires.ftc.vision.VisionProcessor import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.koin.core.qualifier.named -import com.github.serivesmejia.eocvsim.EOCVSim import org.opencv.core.Mat import org.openftc.easyopencv.OpenCvPipeline import org.openftc.easyopencv.OpenCvViewport import org.openftc.easyopencv.processFrameInternal -import java.util.* import kotlin.coroutines.EmptyCoroutineContext import kotlin.math.roundToLong @OptIn(DelicateCoroutinesApi::class) -class PipelineManager : KoinComponent { +class PipelineManager : Orchestrable, KoinComponent { + + private val initOrchestrator: Orchestrator by inject(named("init")) + private val onMainUpdate: EventHandler by inject(named("onMainLoop")) - val onMainUpdate: EventHandler by inject(named("onMainLoop")) - val params: EOCVSim.Parameters by inject() + private val params: EOCVSim.Parameters by inject() - val dialogFactory: DialogFactory by inject() - val configManager: ConfigManager by inject() - val inputSourceManager: InputSourceManager by inject() + private val dialogFactory: DialogFactory by inject() + private val configManager: ConfigManager by inject() + private val inputSourceManager: InputSourceManager by inject() val pipelineStatisticsCalculator: PipelineStatisticsCalculator by inject() - val visualizer: Visualizer by inject() - val classpathScan: ClasspathScan by inject() - val scope: CoroutineScope by inject() + private val visualizer: Visualizer by inject() + private val classpathScan: ClasspathScan by inject() + private val scope: CoroutineScope by inject() val compiledPipelineManager: CompiledPipelineManager by inject() @@ -189,16 +191,19 @@ class PipelineManager : KoinComponent { USER_REQUESTED, IMAGE_ONE_ANALYSIS, NOT_PAUSED } - fun init() { + init { + initOrchestrator.register(this) { + target { it.init() } + dependsOn(classpathScan, compiledPipelineManager) + } + } + + private fun init() { logger.info("Initializing...") //add default pipeline addPipelineClass(DefaultPipeline::class.java) - compiledPipelineManager.init() - - classpathScan.join() - //scan for pipelines for (pipelineClass in classpathScan.scanResult!!.pipelineClasses) { addPipelineClass(pipelineClass) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt index efb4879c..29900366 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt @@ -40,19 +40,22 @@ import java.io.File import org.koin.core.component.KoinComponent import org.koin.core.component.inject -import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.gui.Visualizer -import com.github.serivesmejia.eocvsim.util.ClasspathScan +import com.github.serivesmejia.eocvsim.util.event.Orchestrable +import com.github.serivesmejia.eocvsim.util.event.Orchestrator import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager import org.koin.core.qualifier.named -class CompiledPipelineManager : KoinComponent { +class CompiledPipelineManager : Orchestrable, KoinComponent { + + private val initOrchestrator: Orchestrator by inject(named("init")) + private val onMainLoop: EventHandler by inject(named("onMainLoop")) + private val scope: CoroutineScope by inject() private val pipelineManager: PipelineManager by inject() + val workspaceManager: WorkspaceManager by inject() private val visualizer: Visualizer by inject() private val dialogFactory: DialogFactory by inject() - private val scope: CoroutineScope by inject() - private val onMainLoop: EventHandler by inject(named("onMainLoop")) companion object { val logger by loggerForThis() @@ -98,9 +101,14 @@ class CompiledPipelineManager : KoinComponent { var isBuildRunning = false private set - val workspaceManager: WorkspaceManager by inject() + init { + initOrchestrator.register(this) { + target { it.init() } + dependsOn(workspaceManager) + } + } - fun init() { + private fun init() { logger.info("Initializing...") onBuildStart { @@ -206,12 +214,12 @@ class CompiledPipelineManager : KoinComponent { } } - onBuildEnd.callRightAway = true + onBuildEnd.callRightAway = EventHandler.CallRightAway.InPlace onBuildEnd.run() scope.launch { delay(1000) - onBuildEnd.callRightAway = false + onBuildEnd.callRightAway = EventHandler.CallRightAway.Disabled } isBuildRunning = false diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt index 059a32ff..cb7cf13e 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt @@ -64,6 +64,6 @@ class EOCVSimApiImpl(owner: EOCVSimPlugin) : EOCVSimApi(owner), KoinComponent { override val configApi: ConfigApi by apiField(ConfigApiImpl(owner, configManager)) override fun disableApi() { - logger.info("EOCV-Sim API for {} says: \"ight, imma head out\"", ownerName) + logger.info("API for {} says: \"ight, imma head out\"", ownerName) } } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt index d5446dc5..8011adc9 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt @@ -11,13 +11,15 @@ import org.koin.core.component.KoinComponent import org.koin.core.component.inject import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.gui.Visualizer +import com.github.serivesmejia.eocvsim.util.event.Orchestrable +import com.github.serivesmejia.eocvsim.util.event.Orchestrator +import org.koin.core.qualifier.named -class TunerManager : KoinComponent { - - val pipelineManager: PipelineManager by inject() - - val visualizer: Visualizer by inject() +class TunerManager : Orchestrable, KoinComponent { + private val initOrchestrator: Orchestrator by inject(named("init")) + private val pipelineManager: PipelineManager by inject() + private val visualizer: Visualizer by inject() val logger by loggerForThis() @@ -27,12 +29,19 @@ class TunerManager : KoinComponent { private var firstInit = true - fun init() { - if (firstInit) { - pipelineManager.onPipelineChange.attach { reset() } - firstInit = false + init { + initOrchestrator.register(this) { + target { it.init() } + dependsOn(pipelineManager) } + } + + private fun init() { + pipelineManager.onPipelineChange.attach { reset() } + refreshFields() + } + private fun refreshFields() { pipelineManager.reflectTarget?.let { target -> addFieldsFrom(target) visualizer.updateTunerFields(createTunableFieldPanels()) @@ -68,7 +77,7 @@ class TunerManager : KoinComponent { fun reset() { fields.clear() - init() + refreshFields() } fun newTunableFieldInstanceFor(field: VirtualField, pipeline: Any): TunableField<*>? { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt index b3d784f3..a87d595c 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt @@ -23,22 +23,36 @@ package com.github.serivesmejia.eocvsim.util - import com.qualcomm.robotcore.eventloop.opmode.Disabled +import com.github.serivesmejia.eocvsim.util.event.Orchestrable +import com.github.serivesmejia.eocvsim.util.event.Orchestrator +import com.qualcomm.robotcore.eventloop.opmode.Disabled import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode import com.qualcomm.robotcore.eventloop.opmode.OpMode import com.qualcomm.robotcore.util.ElapsedTime import io.github.classgraph.ClassGraph import io.github.deltacv.common.util.loggerForThis -import kotlinx.coroutines.* import org.firstinspires.ftc.vision.VisionProcessor +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named import org.openftc.easyopencv.OpenCvPipeline +class AutoClasspathScan : ClasspathScan(), Orchestrable, KoinComponent { + val initOrchestrator: Orchestrator by inject(named("init")) + + init { + initOrchestrator.register(this) { + target { it.scan() } + } + } +} + /** * Classpath scanner using ClassGraph. * * It scans for OpenCvPipelines, OpModes, VisionProcessors and TunableFields */ -class ClasspathScan { +open class ClasspathScan { companion object { val ignoredPackages = arrayOf( @@ -65,8 +79,6 @@ class ClasspathScan { var hasScanned = false private set - private lateinit var scanResultJob: Job - /** * Perform the classpath scan using ClassGraph, surprisingly fast due to * the miracles of said library using bytecode scanning instead of reflection. @@ -77,7 +89,11 @@ class ClasspathScan { * @param addProcessorsAsPipelines if true, VisionProcessors will be wrapped as pipelines */ @Suppress("UNCHECKED_CAST") - fun scan(jarFile: String? = null, classLoader: ClassLoader? = null, addProcessorsAsPipelines: Boolean = true): ScanResult { + fun scan( + jarFile: String? = null, + classLoader: ClassLoader? = null, + addProcessorsAsPipelines: Boolean = true + ): ScanResult { val timer = ElapsedTime() val classGraph = ClassGraph() .enableClassInfo() @@ -85,21 +101,21 @@ class ClasspathScan { .enableAnnotationInfo() .rejectPackages(*ignoredPackages) - if(jarFile != null) { + if (jarFile != null) { classGraph.overrideClasspath("$jarFile!/") logger.info("Starting to scan for classes in $jarFile...") } else { logger.info("Starting to scan classpath...") } - if(classLoader != null) { + if (classLoader != null) { classGraph.overrideClassLoaders(classLoader) } val scanResult = classGraph.scan() logger.info("ClassGraph finished scanning (took ${timer.seconds()}s)") - + val pipelineClasses = mutableListOf>() @@ -108,33 +124,33 @@ class ClasspathScan { fun searchPipelinesOfSuperclass(superclass: String) { logger.trace("searchPipelinesOfSuperclass: {}", superclass) - val superclassClazz = if(classLoader != null) { + val superclassClazz = if (classLoader != null) { classLoader.loadClass(superclass) } else Class.forName(superclass) - val pipelineClassesInfo = if(superclassClazz.isInterface) + val pipelineClassesInfo = if (superclassClazz.isInterface) scanResult.getClassesImplementing(superclass) - else scanResult.getSubclasses(superclass) + else scanResult.getSubclasses(superclass) - for(pipelineClassInfo in pipelineClassesInfo) { + for (pipelineClassInfo in pipelineClassesInfo) { logger.trace("pipelineClassInfo: {}", pipelineClassInfo.name) - for(pipelineSubclassInfo in pipelineClassInfo.subclasses) { + for (pipelineSubclassInfo in pipelineClassInfo.subclasses) { searchPipelinesOfSuperclass(pipelineSubclassInfo.name) // naming is my passion } - if(pipelineClassInfo.isAbstract || pipelineClassInfo.isInterface) { + if (pipelineClassInfo.isAbstract || pipelineClassInfo.isInterface) { continue // nope'd outta here } - val clazz = if(classLoader != null) { + val clazz = if (classLoader != null) { classLoader.loadClass(pipelineClassInfo.name) } else Class.forName(pipelineClassInfo.name) logger.trace("class {} super {}", clazz.typeName, clazz.superclass.typeName) - if(!pipelineClasses.contains(clazz) && ReflectUtil.hasSuperclass(clazz, superclassClazz)) { - if(clazz.isAnnotationPresent(Disabled::class.java)) { + if (!pipelineClasses.contains(clazz) && ReflectUtil.hasSuperclass(clazz, superclassClazz)) { + if (clazz.isAnnotationPresent(Disabled::class.java)) { logger.info("Found @Disabled pipeline ${clazz.typeName}") } else { logger.info("Found pipeline ${clazz.typeName}") @@ -147,7 +163,7 @@ class ClasspathScan { // start recursive hell searchPipelinesOfSuperclass(OpenCvPipeline::class.java.name) - if(jarFile != null) { + if (jarFile != null) { // Since we removed EOCV-Sim from the scan classpath, // ClassGraph does not know that OpMode and LinearOpMode // are subclasses of OpenCvPipeline, so we have to scan them @@ -156,7 +172,7 @@ class ClasspathScan { searchPipelinesOfSuperclass(LinearOpMode::class.java.name) } - if(addProcessorsAsPipelines) { + if (addProcessorsAsPipelines) { logger.info("Searching for VisionProcessors...") searchPipelinesOfSuperclass(VisionProcessor::class.java.name) } @@ -166,36 +182,12 @@ class ClasspathScan { logger.info("Finished scanning (took ${timer.seconds()}s)") this.scanResult = ScanResult( - pipelineClasses.toTypedArray() + pipelineClasses ) return this.scanResult!! } - /** - * Asynchronously perform the classpath scan - * and store the result in [scanResult] - */ - @OptIn(DelicateCoroutinesApi::class) - fun asyncScan(scope: CoroutineScope = GlobalScope) { - if(hasScanned) return - hasScanned = true - - scanResultJob = scope.launch(Dispatchers.IO) { - scan() - } - } - - /** - * Join the async scan coroutine - * @see asyncScan - */ - fun join() = runBlocking { - if(::scanResultJob.isInitialized) { - scanResultJob.join() - } - } - } /** @@ -203,5 +195,5 @@ class ClasspathScan { * @param pipelineClasses the found OpenCvPipelines */ data class ScanResult( - val pipelineClasses: Array> + val pipelineClasses: List> ) \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt index 9f223141..a3aaec52 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt @@ -29,6 +29,8 @@ import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.pipeline.compiler.CompiledPipelineManager import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.util.event.Orchestrable +import com.github.serivesmejia.eocvsim.util.event.Orchestrator import com.github.serivesmejia.eocvsim.util.io.FileWatcher import com.github.serivesmejia.eocvsim.workspace.config.WorkspaceConfig import com.github.serivesmejia.eocvsim.workspace.config.WorkspaceConfigLoader @@ -60,13 +62,15 @@ import java.nio.file.Paths * classloader. */ @OptIn(DelicateCoroutinesApi::class) -class WorkspaceManager : KoinComponent { +class WorkspaceManager : Orchestrable, KoinComponent { private val pipelineManager: PipelineManager by inject() private val compiledPipelineManager: CompiledPipelineManager by inject() private val configManager: ConfigManager by inject() private val params: EOCVSim.Parameters by inject() private val scope: CoroutineScope by inject() + + private val initOrchestrator: Orchestrator by inject(named("init")) private val onMainLoop: EventHandler by inject(named("onMainLoop")) val logger by loggerForThis() @@ -212,6 +216,13 @@ class WorkspaceManager : KoinComponent { lateinit var fileWatcher: FileWatcher private set + init { + initOrchestrator.register(this) { + target { it.init() } + dependsOn(configManager) + } + } + /** * Stops the current file watcher, if initialized */ @@ -221,6 +232,27 @@ class WorkspaceManager : KoinComponent { } } + /** + * Initializes the workspace manager + * To be called by the EOCVSim instance + * Initializes the file watcher and the workspace configuration loader + * and sets the workspace file to the initial workspace or the default one + */ + private fun init() { + onWorkspaceChange { + fileWatcher.onChange { + pipelineManager.compiledPipelineManager.asyncBuild() + } + } + + val file = params.initialWorkspace ?: File(configManager.config.workspacePath) + + workspaceFile = if (file.exists()) + file + else + CompiledPipelineManager.DEF_WORKSPACE_FOLDER + } + /** * Creates a new workspace with the specified folder * from a template file bundled with the simulator @@ -260,27 +292,6 @@ class WorkspaceManager : KoinComponent { } } - /** - * Initializes the workspace manager - * To be called by the EOCVSim instance - * Initializes the file watcher and the workspace configuration loader - * and sets the workspace file to the initial workspace or the default one - */ - fun init() { - onWorkspaceChange { - fileWatcher.onChange { - pipelineManager.compiledPipelineManager.asyncBuild() - } - } - - val file = params.initialWorkspace ?: File(configManager.config.workspacePath) - - workspaceFile = if (file.exists()) - file - else - CompiledPipelineManager.DEF_WORKSPACE_FOLDER - } - /** * Saves the current workspace configuration */ 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 0b88e9dd..8d16ba7c 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 @@ -25,6 +25,7 @@ package io.github.deltacv.eocvsim.plugin.loader import com.github.serivesmejia.eocvsim.Build import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.LifecycleSignal import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.dialog.PluginOutput import com.github.serivesmejia.eocvsim.gui.dialog.PluginOutput.Companion.trimSpecials @@ -46,18 +47,23 @@ import org.koin.core.component.inject import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.util.event.Orchestrable +import com.github.serivesmejia.eocvsim.util.event.Orchestrator +import kotlinx.coroutines.channels.Channel import org.koin.core.qualifier.named /** * Manages the loading, enabling and disabling of plugins */ -class PluginManager : KoinComponent { +class PluginManager : Orchestrable, KoinComponent { private val configManager: ConfigManager by inject() private val visualizer: Visualizer by inject() private val dialogFactory: DialogFactory by inject() + + private val initOrchestrator: Orchestrator by inject(named("init")) private val onMainUpdate: EventHandler by inject(named("onMainLoop")) - private val onRestartRequested: EventHandler by inject(named("onRestartRequested")) + private val lifecycleChannel: Channel by inject(named("lifecycle")) companion object { val PLUGIN_FOLDER = io.github.deltacv.eocvsim.plugin.PLUGIN_FOLDER @@ -106,7 +112,7 @@ class PluginManager : KoinComponent { } val repositoryManager by lazy { - PluginRepositoryManager(appender, onMainUpdate, { onRestartRequested.run() }, haltLock, haltCondition) + PluginRepositoryManager(appender, onMainUpdate, { lifecycleChannel.trySend(LifecycleSignal.Restart) }, haltLock, haltCondition) } private val _pluginFiles = mutableListOf() @@ -127,6 +133,13 @@ class PluginManager : KoinComponent { */ val eocvSimApiProvider = EOCVSimApiProvider { plugin -> EOCVSimApiImpl(plugin) } + init { + initOrchestrator.register(this) { + target { it.init() } + dependsOn(configManager) + } + } + /** * Initializes the plugin manager * Loads all plugin files in the plugins folder @@ -134,7 +147,7 @@ class PluginManager : KoinComponent { * and stores them in the loaders map * @see PluginLoader */ - fun init() { + private fun init() { visualizer.onInitFinished { appender.append(PluginOutput.SPECIAL_FREE) } @@ -254,6 +267,8 @@ class PluginManager : KoinComponent { } else { appender.appendln(PluginOutput.SPECIAL_SILENT + "PaperVision plugin is already loaded, skipping embedded plugin.") } + + loadPlugins() } private fun addEmbeddedPlugin( @@ -270,7 +285,7 @@ class PluginManager : KoinComponent { * Loads all plugins * @see PluginLoader.load */ - fun loadPlugins() { + private fun loadPlugins() { for (loader in _loaders.toTypedArray()) { try { val hash = loader.hash() diff --git a/EOCV-Sim/src/test/java/com/github/serivesmejia/eocvsim/startup/StartupOrchestratorTest.kt b/EOCV-Sim/src/test/java/com/github/serivesmejia/eocvsim/startup/StartupOrchestratorTest.kt new file mode 100644 index 00000000..e69de29b diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt b/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt index 24c76db7..c478be35 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt +++ b/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt @@ -60,9 +60,6 @@ class SwingOpenCvViewport( private val needToDeactivateRegardlessOfUser = false private var surfaceExistsAndIsReady = false - @Volatile - private var useGpuCanvas = false - private var isInitialized = false var dark = false @@ -212,6 +209,11 @@ class SwingOpenCvViewport( } } + fun dispose() { + deactivate() + skiaLayer.dispose() + } + override fun setOptimizedViewRotation(rotation: OptimizedRotation) {} override fun notifyStatistics(fps: Float, pipelineMs: Int, overheadMs: Int) { renderer.notifyStatistics(fps, pipelineMs, overheadMs) From 62042c771130529e8140c270d6a926cbc176f28f Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Sat, 25 Apr 2026 14:12:34 -0600 Subject: [PATCH 09/40] Move lifecycle control away from the EOCVSim object into an Orchestrator with phases --- .../eocvsim/util/event/Orchestrator.kt | 501 ++++++++++++++++-- .../github/serivesmejia/eocvsim/EOCVSim.kt | 24 +- .../com/github/serivesmejia/eocvsim/Module.kt | 32 +- .../serivesmejia/eocvsim/config/Config.java | 3 +- .../eocvsim/config/ConfigManager.kt | 30 +- .../serivesmejia/eocvsim/gui/Visualizer.kt | 60 +-- .../gui/component/visualizer/TopMenuBar.kt | 3 +- .../serivesmejia/eocvsim/gui/dialog/Output.kt | 6 +- .../eocvsim/input/InputSourceManager.kt | 23 +- .../eocvsim/pipeline/PipelineManager.kt | 39 +- .../CompiledPipelineManager.kt | 22 +- .../PipelineClassLoader.kt | 3 +- .../PipelineCompiler.kt | 2 +- .../PipelineStandardFileManager.kt | 2 +- .../eocvsim/tuner/TunerManager.kt | 21 +- .../eocvsim/util/ClasspathScan.kt | 14 +- .../eocvsim/workspace/WorkspaceManager.kt | 45 +- .../eocvsim/plugin/loader/PluginManager.kt | 39 +- 18 files changed, 614 insertions(+), 255 deletions(-) rename EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/{compiler => compiled}/CompiledPipelineManager.kt (93%) rename EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/{compiler => compiled}/PipelineClassLoader.kt (92%) rename EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/{compiler => compiled}/PipelineCompiler.kt (96%) rename EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/{compiler => compiled}/PipelineStandardFileManager.kt (95%) diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/Orchestrator.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/Orchestrator.kt index baa40792..06942b9a 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/Orchestrator.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/Orchestrator.kt @@ -3,57 +3,169 @@ package com.github.serivesmejia.eocvsim.util.event import io.github.deltacv.common.util.loggerOf -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.* +import kotlin.reflect.KClass class Orchestrator( + private val scope: CoroutineScope, + val tasks: List = emptyList(), val name: String? = null, - private val scope: CoroutineScope ) { + enum class Phase { + INIT, + RUN, + DESTROY + } + + sealed class Dependency { + data class Instance(val target: Orchestrable) : Dependency() + data class Class(val target: KClass) : Dependency() + } + private val logger by loggerOf("Orchestrator:${name ?: Integer.toHexString(hashCode())}") data class Registration( val instance: Orchestrable, - val target: suspend () -> Unit, - val dependencies: List - ) + val phases: Map + ) { + data class PhaseRegistration( + val target: suspend () -> Unit, + val dependencies: List + ) + } class RegistrationCtx internal constructor( val instance: T ) { - private val dependencyInstances = mutableListOf() - private var targetRunner: (suspend (T) -> Unit)? = null + private data class MutablePhaseRegistration( + val dependencyInstances: MutableList = mutableListOf(), + var targetRunner: (suspend (T) -> Unit)? = null + ) + + private val phaseRegistrations = mutableMapOf>() + private var activePhase: Phase = Phase.INIT + + private fun currentPhaseRegistration(): MutablePhaseRegistration { + return phaseRegistrations.getOrPut(activePhase) { MutablePhaseRegistration() } + } + + fun phase(phase: Phase, block: RegistrationCtx.() -> Unit) { + val previous = activePhase + activePhase = phase + try { + block() + } finally { + activePhase = previous + } + } fun dependsOn(vararg targets: Orchestrable) { for (target in targets) { - if (dependencyInstances.none { it === target }) { - dependencyInstances.add(target) + addDependency(Dependency.Instance(target)) + } + } + + fun dependsOn(vararg targets: KClass) { + for (target in targets) { + addDependency(Dependency.Class(target)) + } + } + + fun dependsOn(vararg dependencies: Dependency) { + for (dependency in dependencies) { + addDependency(dependency) + } + } + + inline fun dependsOn() { + addDependency(Dependency.Class(D::class)) + } + + @PublishedApi + internal fun addDependency(dependency: Dependency) { + val current = currentPhaseRegistration().dependencyInstances + if (current.none { matchesDependency(it, dependency) }) { + current.add(dependency) + } + } + + private fun matchesDependency(left: Dependency, right: Dependency): Boolean { + return when (left) { + is Dependency.Instance -> when (right) { + is Dependency.Instance -> left.target === right.target + is Dependency.Class -> false + } + is Dependency.Class -> when (right) { + is Dependency.Instance -> false + is Dependency.Class -> left.target == right.target } } } - fun target(block: suspend (T) -> Unit) { - targetRunner = block + fun target(block: suspend T.() -> Unit) { + currentPhaseRegistration().targetRunner = block } internal fun build(): Registration { - val runner = targetRunner - ?: throw IllegalArgumentException("register { } requires targetMethod { instance -> ... }") + val builtPhases = phaseRegistrations.mapNotNull { (phase, phaseRegistration) -> + val runner = phaseRegistration.targetRunner ?: return@mapNotNull null + + phase to Registration.PhaseRegistration( + target = { runner(instance) }, + dependencies = phaseRegistration.dependencyInstances.toList() + ) + }.toMap() + + if (builtPhases.isEmpty()) { + throw IllegalArgumentException("register { } requires at least one phase target { ... }") + } return Registration( instance = instance, - target = { runner(instance) }, - dependencies = dependencyInstances.toList() + phases = builtPhases ) } } private val registrations = mutableListOf() + private var tasksWired = false + private var currentPhase: Phase = Phase.INIT + private var runPhaseStarted = false + private val executionGraphLoggedPhases = mutableSetOf() + private var runGraphLogged = false + + fun changePhase(phase: Phase) { + if (phase == currentPhase) { + return + } + + if (currentPhase == Phase.RUN && runPhaseStarted) { + logger.info("Orchestration phase RUN finished successfully") + runPhaseStarted = false + } + + currentPhase = phase + } + + fun getPhase(): Phase = currentPhase + + private fun ensureTasksWired() { + if (tasksWired) { + return + } + + if (tasks.isNotEmpty()) { + logger.debug("Wiring ${tasks.size} task(s) into orchestrator") + } + + tasks.forEach { task -> + task.wire(this) + } + + tasksWired = true + } fun register(targetInstance: T, block: RegistrationCtx.() -> Unit = {}) { val registration = RegistrationCtx(targetInstance).apply(block).build() @@ -70,46 +182,63 @@ class Orchestrator( registrations.add(registration) logger.debug( - "Registered target ${registration.instance::class.qualifiedName} with ${registration.dependencies.size} dependency(ies)" + "Registered target ${registration.instance::class.qualifiedName} with ${registration.phases.size} phase(s)" ) } fun orchestrate() { + ensureTasksWired() + + val phase = currentPhase + runBlocking(scope.coroutineContext) { - logger.info("Starting orchestration with ${registrations.size} registration(s)") - validateDependenciesExist() + val verboseDebug = phase != Phase.RUN + val logLifecycle = beginLifecycleLog(phase) + + val phaseRegistrations = activeRegistrationsFor(phase) + + logExecutionGraphOnce(phase, phaseRegistrations) + + if (logLifecycle) { + logger.info("Starting orchestration phase $phase with ${phaseRegistrations.size} registration(s)") + } + validateDependenciesExist(phase, phaseRegistrations, verboseDebug) - val pending = registrations.toMutableList() - val executedTargets = mutableListOf() + val pending = phaseRegistrations.toMutableList() + val executedTargets = mutableListOf() var wave = 0 while (pending.isNotEmpty()) { wave++ val ready = pending.filter { registration -> - registration.dependencies.all { dep -> executedTargets.any { it === dep } } + registration.dependencies.all { dependency -> + executedTargets.any { executed -> matchesDependency(executed, dependency) } + } } - logger.debug( - "Wave $wave -> pending=${pending.size}, ready=${ready.size}, executed=${executedTargets.size}" - ) + if (verboseDebug) { + logger.debug( + "Wave $wave -> pending=${pending.size}, ready=${ready.size}, executed=${executedTargets.size}" + ) + } - if (ready.isNotEmpty()) { + if (verboseDebug && ready.isNotEmpty()) { val readyNames = ready.joinToString { it.instance::class.qualifiedName ?: "" } logger.debug("Wave $wave ready targets: $readyNames") } if (ready.isEmpty()) { val remaining = pending.joinToString { it.instance::class.qualifiedName ?: "" } - logger.error("Deadlock/cycle detected, remaining: $remaining") + logger.error("Deadlock/cycle detected in phase $phase, remaining: $remaining") throw IllegalStateException( - "Circular dependency detected among remaining targets: $remaining" + "Circular dependency detected in phase $phase among remaining targets: $remaining" ) } // Execute this dependency wave in parallel. - runBatchInParallel(ready, wave) + runBatchInParallel(ready, wave, verboseDebug) for (registration in ready) { executedTargets.add(registration.instance) @@ -117,54 +246,186 @@ class Orchestrator( } } - logger.info("Orchestration finished successfully") + if (logLifecycle && phase != Phase.RUN) { + logger.info("Orchestration phase $phase finished successfully") + } + } + } + + fun orchestrate(phase: Phase) { + changePhase(phase) + orchestrate() + } + + private fun beginLifecycleLog(phase: Phase): Boolean { + if (phase != Phase.RUN) { + return true + } + + if (runPhaseStarted) { + return false + } + + runPhaseStarted = true + return true + } + + private fun activeRegistrationsFor(phase: Phase): List { + return registrations.mapNotNull { registration -> + val phaseRegistration = registration.phases[phase] ?: return@mapNotNull null + ActiveRegistration( + instance = registration.instance, + target = phaseRegistration.target, + dependencies = phaseRegistration.dependencies + ) } } - private suspend fun runBatchInParallel(registrations: List, wave: Int) = coroutineScope { + private fun logExecutionGraphOnce(phase: Phase, phaseRegistrations: List) { + if (phase in executionGraphLoggedPhases) { + return + } + + executionGraphLoggedPhases.add(phase) + + logger.debug("Execution graph plan for phase {}:", phase) + logger.debug("Phase {}: {} target(s)", phase, phaseRegistrations.size) + + if (phaseRegistrations.isEmpty()) { + return + } + + val pending = phaseRegistrations.toMutableList() + val executedTargets = mutableListOf() + var wave = 0 + + while (pending.isNotEmpty()) { + wave++ + + val ready = pending.filter { registration -> + registration.dependencies.all { dependency -> + executedTargets.any { executed -> matchesDependency(executed, dependency) } + } + } + + if (ready.isEmpty()) { + val remaining = pending.joinToString { it.instance::class.qualifiedName ?: "" } + logger.debug("Phase {} wave {}: remaining=[{}]", phase, wave, remaining) + break + } + + val readyNames = ready.joinToString { it.instance::class.qualifiedName ?: "" } + logger.debug("Phase {} wave {} (parallel={}): [{}]", phase, wave, ready.size, readyNames) + + for (registration in ready) { + executedTargets.add(registration.instance) + pending.remove(registration) + } + } + } + + private fun logRunGraphOnce(phaseRegistrations: List) { + if (runGraphLogged) { + return + } + + runGraphLogged = true + + logger.debug("RUN graph has ${phaseRegistrations.size} registration(s)") + phaseRegistrations.forEach { registration -> + val target = registration.instance::class.qualifiedName ?: "" + val deps = if (registration.dependencies.isEmpty()) { + "" + } else { + registration.dependencies.joinToString { dependency -> + when (dependency) { + is Dependency.Instance -> dependency.target::class.qualifiedName ?: "" + is Dependency.Class -> dependency.target.qualifiedName ?: "" + } + } + } + + logger.debug("RUN node: $target dependsOn [$deps]") + } + } + + private data class ActiveRegistration( + val instance: Orchestrable, + val target: suspend () -> Unit, + val dependencies: List + ) + + private suspend fun runBatchInParallel( + registrations: List, + wave: Int, + verboseDebug: Boolean + ) = coroutineScope { val start = System.nanoTime() - logger.debug("Wave $wave launching ${registrations.size} parallel target(s)") + if (verboseDebug) { + logger.debug("Wave $wave launching ${registrations.size} parallel target(s)") + } registrations.map { registration -> async { val targetName = registration.instance::class.qualifiedName ?: "" val targetStart = System.nanoTime() - logger.debug("Wave $wave start target $targetName") + if (verboseDebug) { + logger.debug("Wave $wave start target $targetName") + } invokeTarget(registration) val targetMs = (System.nanoTime() - targetStart) / 1_000_000 - logger.debug("Wave $wave finished target $targetName in ${targetMs}ms") + if (verboseDebug) { + logger.debug("Wave $wave finished target $targetName in ${targetMs}ms") + } } }.awaitAll() val batchMs = (System.nanoTime() - start) / 1_000_000 - logger.debug("Wave $wave completed in ${batchMs}ms") + if (verboseDebug) { + logger.debug("Wave $wave completed in ${batchMs}ms") + } } - private fun validateDependenciesExist() { - logger.debug("Validating dependencies") + private fun validateDependenciesExist( + phase: Phase, + phaseRegistrations: List, + verboseDebug: Boolean + ) { + if (verboseDebug) { + logger.debug("Validating dependencies for phase {}", phase) + } - for (registration in registrations) { + for (registration in phaseRegistrations) { for (dependency in registration.dependencies) { - val exists = registrations.any { it.instance === dependency } + val exists = phaseRegistrations.any { candidate -> + matchesDependency(candidate.instance, dependency) + } if (!exists) { + val dependencyName = when (dependency) { + is Dependency.Instance -> dependency.target::class.qualifiedName + is Dependency.Class -> dependency.target.qualifiedName + } + logger.error( - "Unresolved dependency ${dependency::class.qualifiedName} required by ${registration.instance::class.qualifiedName}" + "Unresolved dependency $dependencyName required by ${registration.instance::class.qualifiedName} in phase $phase" ) throw IllegalStateException( - "Unresolved dependency ${dependency::class.simpleName} required by ${registration.instance::class.simpleName}" + "Unresolved dependency $dependencyName required by ${registration.instance::class.simpleName} in phase $phase" ) } } } - logger.debug("Dependency validation passed") + if (verboseDebug) { + logger.debug("Dependency validation passed for phase {}", phase) + } } - private suspend fun invokeTarget(registration: Registration) { + private suspend fun invokeTarget(registration: ActiveRegistration) { try { registration.target() } catch (ex: Exception) { @@ -177,7 +438,155 @@ class Orchestrator( ) } } + + private fun matchesDependency(candidate: Orchestrable, dependency: Dependency): Boolean { + return when (dependency) { + is Dependency.Instance -> candidate === dependency.target + is Dependency.Class -> dependency.target.isInstance(candidate) + } + } } interface Orchestrable { + fun wire(orchestrator: Orchestrator) +} + +interface PhaseOrchestrable : Orchestrable { + val initDependencies: List + get() = emptyList() + + val runDependencies: List + get() = emptyList() + + val destroyDependencies: List + get() = emptyList() + + suspend fun init() + suspend fun run() + suspend fun destroy() + + override fun wire(orchestrator: Orchestrator) { + orchestrator.register(this) { + phase(Orchestrator.Phase.INIT) { + target { init() } + dependsOn(*initDependencies.toTypedArray()) + } + phase(Orchestrator.Phase.RUN) { + target { run() } + dependsOn(*runDependencies.toTypedArray()) + } + + phase(Orchestrator.Phase.DESTROY) { + target { destroy() } + dependsOn(*destroyDependencies.toTypedArray()) + } + } + } } + +abstract class MagicPhaseOrchestrable : PhaseOrchestrable { + + private val _initDependencies = mutableListOf() + override val initDependencies = _initDependencies + + private val _runDependencies = mutableListOf() + override val runDependencies = _runDependencies + + private val _destroyDependencies = mutableListOf() + override val destroyDependencies = _destroyDependencies + + /** + * @param phase if empty, will bind to all phases + */ + fun dependency(target: KClass, vararg phase: Orchestrator.Phase): KClass { + if(phase.isEmpty()) { + initDependency(target) + runDependency(target) + destroyDependency(target) + } else { + for (p in phase) { + when (p) { + Orchestrator.Phase.INIT -> initDependency(target) + Orchestrator.Phase.RUN -> runDependency(target) + Orchestrator.Phase.DESTROY -> destroyDependency(target) + } + } + } + + return target + } + + fun dependency(target: T, vararg phase: Orchestrator.Phase): T { + if(phase.isEmpty()) { + initDependency(target) + runDependency(target) + destroyDependency(target) + } else { + for (p in phase) { + when (p) { + Orchestrator.Phase.INIT -> initDependency(target) + Orchestrator.Phase.RUN -> runDependency(target) + Orchestrator.Phase.DESTROY -> destroyDependency(target) + } + } + } + + return target + } + + inline fun dependency(target: Lazy, vararg phase: Orchestrator.Phase): Lazy { + dependency(T::class, *phase) + return target + } + + // -- INIT DEPENDENCY SUGAR -- + + fun initDependency(target: KClass): KClass { + initDependencies.add(Orchestrator.Dependency.Class(target)) + return target + } + + fun initDependency(target: T): T { + initDependencies.add(Orchestrator.Dependency.Instance(target)) + return target + } + + inline fun initDependency(target: Lazy): Lazy { + initDependency(T::class) + return target + } + + // -- RUN DEPENDENCY SUGAR -- + + fun runDependency(target: KClass): KClass { + runDependencies.add(Orchestrator.Dependency.Class(target)) + return target + } + + fun runDependency(target: T): T { + runDependencies.add(Orchestrator.Dependency.Instance(target)) + return target + } + + inline fun runDependency(target: Lazy): Lazy { + runDependency(T::class) + return target + } + + // -- DESTROY DEPENDENCY SUGAR -- + + fun destroyDependency(target: KClass): KClass { + destroyDependencies.add(Orchestrator.Dependency.Class(target)) + return target + } + + fun destroyDependency(target: T): T { + destroyDependencies.add(Orchestrator.Dependency.Instance(target)) + return target + } + + inline fun destroyDependency(target: Lazy): Lazy { + destroyDependency(T::class) + return target + } +} \ No newline at end of file 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 b406feab..a51524a4 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 @@ -134,7 +134,7 @@ class EOCVSim : KoinComponent { val parameters: Parameters by inject() - val initOrchestrator: Orchestrator by inject(named("init")) + val orchestrator: Orchestrator by inject() /** * Event handler for the main update loop @@ -246,7 +246,8 @@ class EOCVSim : KoinComponent { //loading native lib only once in the app runtime loadOpenCvLib(parameters.opencvNativeLibrary) - initOrchestrator.orchestrate() + orchestrator.changePhase(Orchestrator.Phase.INIT) + orchestrator.orchestrate() visualizer.onInitFinished { // SHOW WELCOME DIALOGS TO NEW USERS @@ -341,17 +342,15 @@ class EOCVSim : KoinComponent { logger.info("-- Begin EOCVSim loop ($hexCode) --") + orchestrator.changePhase(Orchestrator.Phase.RUN) + while (!eocvSimThread.isInterrupted && !destroying) { //run all pending requested runnables onMainLoop.run() pipelineStatisticsCalculator.newInputFrameStart() - inputSourceManager.update(pipelineManager.paused) - tunerManager.update() - - val lastMat = inputSourceManager.lastMatFromSource - pipelineManager.update(lastMat) + orchestrator.orchestrate() //limit FPS fpsLimiter.maxFPS = config.pipelineMaxFps.fps.toDouble() @@ -384,17 +383,10 @@ class EOCVSim : KoinComponent { fun destroy(reason: LifecycleSignal.Destroy.Reason) { logger.warn("-- Destroying current EOCVSim ($hexCode) due to $reason, it is normal to see InterruptedExceptions and other kinds of stack traces below --") - pluginManager.disablePlugins() - - //stop recording session if there's currently an ongoing one - recordingManager.stopRecordingSession() - logger.info("Trying to save config file...") - inputSourceManager.currentInputSource?.close() - workspaceManager.stopFileWatcher() - configManager.saveToFile() - visualizer.close() + orchestrator.changePhase(Orchestrator.Phase.DESTROY) + orchestrator.orchestrate() destroying = true scope.cancel() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt index 71c8993d..cde10972 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt @@ -7,11 +7,11 @@ import com.github.serivesmejia.eocvsim.input.InputSourceInitializer import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.github.serivesmejia.eocvsim.output.RecordingManager import com.github.serivesmejia.eocvsim.pipeline.PipelineManager -import com.github.serivesmejia.eocvsim.pipeline.compiler.CompiledPipelineManager +import com.github.serivesmejia.eocvsim.pipeline.compiled.CompiledPipelineManager import com.github.serivesmejia.eocvsim.tuner.TunerManager -import com.github.serivesmejia.eocvsim.util.AutoClasspathScan -import com.github.serivesmejia.eocvsim.util.ClasspathScan +import com.github.serivesmejia.eocvsim.util.InitClasspathScan import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.util.event.Orchestrable import com.github.serivesmejia.eocvsim.util.event.Orchestrator import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator @@ -20,31 +20,35 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.channels.Channel +import org.koin.core.definition.KoinDefinition import org.koin.core.qualifier.named +import org.koin.dsl.bind import org.koin.dsl.module val eocvSimModule = module { single { PipelineStatisticsCalculator() } - single { AutoClasspathScan() } + single { InitClasspathScan() }.bindOrchestrable() // global scope for launching coroutines within the app single { CoroutineScope(SupervisorJob() + Dispatchers.Default) } - single { ConfigManager() } - single { WorkspaceManager() } - single { PipelineManager() } - single { InputSourceManager() } + single { ConfigManager() }.bindOrchestrable() + single { WorkspaceManager() }.bindOrchestrable() + single { PipelineManager() }.bindOrchestrable() + single { InputSourceManager() }.bindOrchestrable() single { InputSourceInitializer() } - single { TunerManager() } - single { PluginManager() } - single { CompiledPipelineManager() } + single { TunerManager() }.bindOrchestrable() + single { PluginManager() }.bindOrchestrable() + single { CompiledPipelineManager() }.bindOrchestrable() - single { Visualizer() } + single { Visualizer() }.bindOrchestrable() single { DialogFactory() } single { RecordingManager() } - single(named("init")) { Orchestrator("Init", scope = get()) } + single { Orchestrator(scope = get(), tasks = getAll(), name = "Main") } single(named("lifecycle")) { Channel(Channel.BUFFERED) } single(named("onMainLoop")) { EventHandler("OnMainLoop") } -} \ No newline at end of file +} + +private fun KoinDefinition.bindOrchestrable() = this bind Orchestrable::class \ No newline at end of file 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 7064d338..46a72799 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 @@ -29,8 +29,7 @@ import com.github.serivesmejia.eocvsim.gui.util.WebcamDriver; import com.github.serivesmejia.eocvsim.pipeline.PipelineFps; import com.github.serivesmejia.eocvsim.pipeline.PipelineTimeout; -import com.github.serivesmejia.eocvsim.pipeline.compiler.CompiledPipelineManager; -import com.github.serivesmejia.eocvsim.util.SysUtil; +import com.github.serivesmejia.eocvsim.pipeline.compiled.CompiledPipelineManager; import org.opencv.core.Size; import java.util.ArrayList; diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt index a968f324..dc2e8e1c 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt @@ -23,16 +23,12 @@ package com.github.serivesmejia.eocvsim.config -import com.github.serivesmejia.eocvsim.util.event.EventHandler -import com.github.serivesmejia.eocvsim.util.event.Orchestrable -import com.github.serivesmejia.eocvsim.util.event.Orchestrator +import com.github.serivesmejia.eocvsim.util.event.MagicPhaseOrchestrable +import com.github.serivesmejia.eocvsim.util.event.PhaseOrchestrable import io.github.deltacv.common.util.loggerForThis import org.koin.core.component.KoinComponent -import org.koin.core.component.inject -import org.koin.core.qualifier.named - -class ConfigManager : Orchestrable, KoinComponent { +class ConfigManager : MagicPhaseOrchestrable(), KoinComponent { val configLoader = ConfigLoader() var config: Config = Config() @@ -40,15 +36,7 @@ class ConfigManager : Orchestrable, KoinComponent { private val logger by loggerForThis() - private val initOrchestrator: Orchestrator by inject(named("init")) - - init { - initOrchestrator.register(this) { - target { it.init() } - } - } - - private fun init() { + override suspend fun init() { logger.info("Initializing...") try { @@ -65,15 +53,15 @@ class ConfigManager : Orchestrable, KoinComponent { logger.info("Creating config file...") configLoader.saveToFile(config) } + } - Runtime.getRuntime().addShutdownHook(Thread { - logger.info("SHUTDOWN - Saving config to file...") - saveToFile() - }) + override suspend fun run() { } + + override suspend fun destroy() { + saveToFile() } fun saveToFile() { configLoader.saveToFile(config) } - } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt index 15554c9d..8100d6fd 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt @@ -39,15 +39,30 @@ import com.github.serivesmejia.eocvsim.gui.component.visualizer.opmode.SidebarOp import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.PipelineSelectorPanel import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SidebarPipelineTabPanel import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SourceSelectorPanel -import com.github.serivesmejia.eocvsim.gui.theme.Theme import com.github.serivesmejia.eocvsim.input.InputSourceManager -import com.github.serivesmejia.eocvsim.pipeline.compiler.PipelineCompiler +import com.github.serivesmejia.eocvsim.output.RecordingManager +import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import com.github.serivesmejia.eocvsim.pipeline.compiled.PipelineCompiler import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.util.event.MagicPhaseOrchestrable +import com.github.serivesmejia.eocvsim.util.event.Orchestrator +import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager import com.github.serivesmejia.eocvsim.workspace.util.VSCodeLauncher import com.github.serivesmejia.eocvsim.workspace.util.template.GradleWorkspaceTemplate +import com.qualcomm.robotcore.eventloop.opmode.OpMode +import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator import io.github.deltacv.common.util.loggerForThis import io.github.deltacv.vision.external.gui.SwingOpenCvViewport +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.swing.Swing +import kotlinx.coroutines.withContext +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named import org.opencv.core.Size +import org.openftc.easyopencv.OpenCvViewport import java.awt.BorderLayout import java.awt.Dimension import java.awt.Taskbar @@ -56,24 +71,8 @@ import java.awt.event.MouseEvent import java.awt.event.WindowAdapter import java.awt.event.WindowEvent import javax.swing.* -import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager -import com.github.serivesmejia.eocvsim.pipeline.PipelineManager -import com.qualcomm.robotcore.eventloop.opmode.OpMode -import com.github.serivesmejia.eocvsim.output.RecordingManager -import com.github.serivesmejia.eocvsim.util.event.Orchestrable -import com.github.serivesmejia.eocvsim.util.event.Orchestrator -import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator -import org.openftc.easyopencv.OpenCvViewport -import org.koin.core.qualifier.named -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.swing.Swing -import kotlinx.coroutines.withContext -class Visualizer : Orchestrable, KoinComponent { +class Visualizer : MagicPhaseOrchestrable(), KoinComponent { val onMainUpdate: EventHandler by inject(named("onMainLoop")) @@ -81,7 +80,7 @@ class Visualizer : Orchestrable, KoinComponent { val pipelineManager: PipelineManager by inject() val inputSourceManager: InputSourceManager by inject() - val configManager: ConfigManager by inject() + val configManager: ConfigManager by initDependency(inject()) val workspaceManager: WorkspaceManager by inject() val dialogFactory: DialogFactory by inject() val recordingManager: RecordingManager by inject() @@ -152,22 +151,11 @@ class Visualizer : Orchestrable, KoinComponent { } } - init { - initOrchestrator.register(this) { - target { - withContext(Dispatchers.Swing) { - it.init(configManager.config.simTheme) - } - } - dependsOn(configManager) - } - } - - private fun init(theme: Theme) { + override suspend fun init() = withContext(Dispatchers.Swing) { try { - theme.install() + configManager.config.simTheme.install() } catch (e: Exception) { - logger.error("Failed to install theme ${theme.name}", e) + logger.error("Failed to install theme ${configManager.config.simTheme.name}", e) } Icons.setDark(FlatLaf.isLafDark()) @@ -282,6 +270,8 @@ class Visualizer : Orchestrable, KoinComponent { setupSubscriptions() } + override suspend fun run() { } + private fun registerListeners() { frame.addWindowListener(object : WindowAdapter() { override fun windowClosing(e: WindowEvent) { @@ -306,7 +296,7 @@ class Visualizer : Orchestrable, KoinComponent { } } - fun close() { + override suspend fun destroy() { SwingUtilities.invokeLater { frame.isVisible = false viewport.dispose() 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 61918eb8..3ae2c809 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 @@ -23,7 +23,6 @@ package com.github.serivesmejia.eocvsim.gui.component.visualizer -import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.LifecycleSignal import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.Visualizer @@ -31,7 +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.pipeline.compiled.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 diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Output.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Output.kt index 7fbbfff8..b21b4d4f 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Output.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Output.kt @@ -23,16 +23,14 @@ package com.github.serivesmejia.eocvsim.gui.dialog -import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.gui.dialog.component.OutputPanel import com.github.serivesmejia.eocvsim.pipeline.PipelineManager -import com.github.serivesmejia.eocvsim.pipeline.compiler.CompiledPipelineManager -import com.github.serivesmejia.eocvsim.pipeline.compiler.PipelineCompileStatus +import com.github.serivesmejia.eocvsim.pipeline.compiled.CompiledPipelineManager +import com.github.serivesmejia.eocvsim.pipeline.compiled.PipelineCompileStatus import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlinx.coroutines.swing.Swing import java.awt.Dimension diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt index 4f03fd63..dd9a322d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt @@ -31,8 +31,8 @@ import com.github.serivesmejia.eocvsim.input.source.NullSource import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler -import com.github.serivesmejia.eocvsim.util.event.Orchestrable -import com.github.serivesmejia.eocvsim.util.event.Orchestrator +import com.github.serivesmejia.eocvsim.util.event.MagicPhaseOrchestrable +import com.github.serivesmejia.eocvsim.util.event.PhaseOrchestrable import io.github.deltacv.common.util.loggerForThis import kotlinx.coroutines.Job import org.koin.core.component.KoinComponent @@ -47,14 +47,13 @@ import java.io.IOException import java.util.concurrent.CancellationException import javax.swing.JDialog -class InputSourceManager : Orchestrable, KoinComponent { +class InputSourceManager : MagicPhaseOrchestrable(), KoinComponent { private val pipelineManager: PipelineManager by inject() private val configManager: ConfigManager by inject() private val visualizer: Visualizer by inject() private val dialogFactory: DialogFactory by inject() - private val initOrchestrator: Orchestrator by inject(named("init")) private val onMainLoop: EventHandler by inject(named("onMainLoop")) private val inputSourceInitializer: InputSourceInitializer by inject() @@ -83,13 +82,7 @@ class InputSourceManager : Orchestrable, KoinComponent { private val logger by loggerForThis() - init { - initOrchestrator.register(this) { - target { it.init() } - } - } - - private fun init() { + override suspend fun init() { logger.info("Initializing...") matRecycler = MatRecycler(4) @@ -141,7 +134,9 @@ class InputSourceManager : Orchestrable, KoinComponent { } } - fun update(isPaused: Boolean) { + override suspend fun run() { + val isPaused = pipelineManager.paused + val currentSource = currentInputSource ?: return try { @@ -173,6 +168,10 @@ class InputSourceManager : Orchestrable, KoinComponent { } } + override suspend fun destroy() { + currentInputSource?.close() + } + @JvmOverloads fun addInputSource(name: String, inputSource: InputSource?, dispatchedByUser: Boolean = false) { if (inputSource == null) return diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt index 7a0e6523..87a5c126 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt @@ -28,7 +28,7 @@ import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.input.InputSourceManager -import com.github.serivesmejia.eocvsim.pipeline.compiler.CompiledPipelineManager +import com.github.serivesmejia.eocvsim.pipeline.compiled.CompiledPipelineManager import com.github.serivesmejia.eocvsim.pipeline.handler.PipelineHandler import com.github.serivesmejia.eocvsim.pipeline.instantiator.DefaultPipelineInstantiator import com.github.serivesmejia.eocvsim.pipeline.instantiator.PipelineInstantiator @@ -36,13 +36,12 @@ import com.github.serivesmejia.eocvsim.pipeline.instantiator.processor.Processor import com.github.serivesmejia.eocvsim.pipeline.util.PipelineExceptionTracker import com.github.serivesmejia.eocvsim.pipeline.util.PipelineSnapshot import com.github.serivesmejia.eocvsim.tuner.TunableFieldRegistry -import com.github.serivesmejia.eocvsim.util.ClasspathScan +import com.github.serivesmejia.eocvsim.util.InitClasspathScan import com.github.serivesmejia.eocvsim.util.ReflectUtil import com.github.serivesmejia.eocvsim.util.StrUtil import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler -import com.github.serivesmejia.eocvsim.util.event.Orchestrable -import com.github.serivesmejia.eocvsim.util.event.Orchestrator +import com.github.serivesmejia.eocvsim.util.event.MagicPhaseOrchestrable import com.github.serivesmejia.eocvsim.util.fps.FpsCounter import io.github.deltacv.common.image.MatPoster import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator @@ -57,7 +56,6 @@ import org.firstinspires.ftc.vision.VisionProcessor import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.koin.core.qualifier.named -import org.opencv.core.Mat import org.openftc.easyopencv.OpenCvPipeline import org.openftc.easyopencv.OpenCvViewport import org.openftc.easyopencv.processFrameInternal @@ -65,22 +63,22 @@ import kotlin.coroutines.EmptyCoroutineContext import kotlin.math.roundToLong @OptIn(DelicateCoroutinesApi::class) -class PipelineManager : Orchestrable, KoinComponent { - - private val initOrchestrator: Orchestrator by inject(named("init")) - private val onMainUpdate: EventHandler by inject(named("onMainLoop")) +class PipelineManager : MagicPhaseOrchestrable(), KoinComponent { private val params: EOCVSim.Parameters by inject() + private val classpathScan: InitClasspathScan by initDependency(inject()) + private val dialogFactory: DialogFactory by inject() private val configManager: ConfigManager by inject() - private val inputSourceManager: InputSourceManager by inject() + private val inputSourceManager: InputSourceManager by runDependency(inject()) val pipelineStatisticsCalculator: PipelineStatisticsCalculator by inject() private val visualizer: Visualizer by inject() - private val classpathScan: ClasspathScan by inject() + + private val onMainUpdate: EventHandler by inject(named("onMainLoop")) private val scope: CoroutineScope by inject() - val compiledPipelineManager: CompiledPipelineManager by inject() + val compiledPipelineManager: CompiledPipelineManager by initDependency(inject()) companion object { var staticSnapshot: PipelineSnapshot? = null @@ -191,14 +189,7 @@ class PipelineManager : Orchestrable, KoinComponent { USER_REQUESTED, IMAGE_ONE_ANALYSIS, NOT_PAUSED } - init { - initOrchestrator.register(this) { - target { it.init() } - dependsOn(classpathScan, compiledPipelineManager) - } - } - - private fun init() { + override suspend fun init() { logger.info("Initializing...") //add default pipeline @@ -300,7 +291,7 @@ class PipelineManager : Orchestrable, KoinComponent { private var wasBuildRunning = false - fun update(inputMat: Mat?) { + override suspend fun run() { val telemetry = currentTelemetry onUpdate.run() @@ -338,6 +329,8 @@ class PipelineManager : Orchestrable, KoinComponent { "processFrame" } + val inputMat = inputSourceManager.lastMatFromSource + pipelineStatisticsCalculator.newPipelineFrameStart() //run our pipeline in the background until it finishes or gets cancelled @@ -456,6 +449,10 @@ class PipelineManager : Orchestrable, KoinComponent { } } + override suspend fun destroy() { + // TODO: check if we need to do anything here + } + private fun updateExceptionTracker(ex: Throwable? = null) { pipelineExceptionTracker.update( currentPipelineData ?: return, ex diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/CompiledPipelineManager.kt similarity index 93% rename from EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt rename to EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/CompiledPipelineManager.kt index 29900366..a82a36b1 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/CompiledPipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/CompiledPipelineManager.kt @@ -21,7 +21,7 @@ * */ -package com.github.serivesmejia.eocvsim.pipeline.compiler +package com.github.serivesmejia.eocvsim.pipeline.compiled import com.github.serivesmejia.eocvsim.Build import com.github.serivesmejia.eocvsim.gui.DialogFactory @@ -41,19 +41,19 @@ import java.io.File import org.koin.core.component.KoinComponent import org.koin.core.component.inject import com.github.serivesmejia.eocvsim.gui.Visualizer +import com.github.serivesmejia.eocvsim.util.event.MagicPhaseOrchestrable import com.github.serivesmejia.eocvsim.util.event.Orchestrable import com.github.serivesmejia.eocvsim.util.event.Orchestrator import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager import org.koin.core.qualifier.named -class CompiledPipelineManager : Orchestrable, KoinComponent { +class CompiledPipelineManager : MagicPhaseOrchestrable(), KoinComponent { - private val initOrchestrator: Orchestrator by inject(named("init")) private val onMainLoop: EventHandler by inject(named("onMainLoop")) private val scope: CoroutineScope by inject() private val pipelineManager: PipelineManager by inject() - val workspaceManager: WorkspaceManager by inject() + val workspaceManager: WorkspaceManager by initDependency(inject()) private val visualizer: Visualizer by inject() private val dialogFactory: DialogFactory by inject() @@ -101,14 +101,7 @@ class CompiledPipelineManager : Orchestrable, KoinComponent { var isBuildRunning = false private set - init { - initOrchestrator.register(this) { - target { it.init() } - dependsOn(workspaceManager) - } - } - - private fun init() { + override suspend fun init() { logger.info("Initializing...") onBuildStart { @@ -132,6 +125,10 @@ class CompiledPipelineManager : Orchestrable, KoinComponent { } } + override suspend fun run() { } + + override suspend fun destroy() { } + @OptIn(DelicateCoroutinesApi::class) suspend fun uncheckedBuild(): PipelineCompileResult { if(isBuildRunning) return PipelineCompileResult( @@ -268,7 +265,6 @@ class CompiledPipelineManager : Orchestrable, KoinComponent { val isCompilerSupported get() = PipelineCompiler.IS_USABLE - private fun deleteJarFile() { if(PIPELINES_OUTPUT_JAR.exists()) PIPELINES_OUTPUT_JAR.delete() currentPipelineClassLoader = null diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/PipelineClassLoader.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineClassLoader.kt similarity index 92% rename from EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/PipelineClassLoader.kt rename to EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineClassLoader.kt index e4137f28..ba49acc8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/PipelineClassLoader.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineClassLoader.kt @@ -1,11 +1,10 @@ -package com.github.serivesmejia.eocvsim.pipeline.compiler +package com.github.serivesmejia.eocvsim.pipeline.compiled import com.github.serivesmejia.eocvsim.util.ClasspathScan import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.extension.removeFromEnd import io.github.deltacv.eocvsim.sandbox.restrictions.MethodCallByteCodeChecker import io.github.deltacv.eocvsim.sandbox.restrictions.dynamicCodeMethodBlacklist -import io.github.deltacv.eocvsim.sandbox.restrictions.dynamicCodePackageBlacklist import org.openftc.easyopencv.OpenCvPipeline import java.io.ByteArrayOutputStream import java.io.File diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/PipelineCompiler.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineCompiler.kt similarity index 96% rename from EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/PipelineCompiler.kt rename to EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineCompiler.kt index d5ade4e5..837aef3e 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/PipelineCompiler.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineCompiler.kt @@ -21,7 +21,7 @@ * */ -package com.github.serivesmejia.eocvsim.pipeline.compiler +package com.github.serivesmejia.eocvsim.pipeline.compiled import com.github.serivesmejia.eocvsim.util.* import com.github.serivesmejia.eocvsim.util.compiler.JarPacker diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/PipelineStandardFileManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineStandardFileManager.kt similarity index 95% rename from EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/PipelineStandardFileManager.kt rename to EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineStandardFileManager.kt index f15c0904..686f1ba6 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiler/PipelineStandardFileManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineStandardFileManager.kt @@ -21,7 +21,7 @@ * */ -package com.github.serivesmejia.eocvsim.pipeline.compiler +package com.github.serivesmejia.eocvsim.pipeline.compiled import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.compiler.DelegatingStandardFileManager diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt index 8011adc9..1781f20a 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt @@ -11,14 +11,14 @@ import org.koin.core.component.KoinComponent import org.koin.core.component.inject import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.gui.Visualizer +import com.github.serivesmejia.eocvsim.util.event.MagicPhaseOrchestrable import com.github.serivesmejia.eocvsim.util.event.Orchestrable import com.github.serivesmejia.eocvsim.util.event.Orchestrator import org.koin.core.qualifier.named -class TunerManager : Orchestrable, KoinComponent { +class TunerManager : MagicPhaseOrchestrable(), KoinComponent { - private val initOrchestrator: Orchestrator by inject(named("init")) - private val pipelineManager: PipelineManager by inject() + private val pipelineManager: PipelineManager by dependency(inject()) private val visualizer: Visualizer by inject() val logger by loggerForThis() @@ -29,14 +29,7 @@ class TunerManager : Orchestrable, KoinComponent { private var firstInit = true - init { - initOrchestrator.register(this) { - target { it.init() } - dependsOn(pipelineManager) - } - } - - private fun init() { + override suspend fun init() { pipelineManager.onPipelineChange.attach { reset() } refreshFields() } @@ -59,7 +52,7 @@ class TunerManager : Orchestrable, KoinComponent { } } - fun update() { + override suspend fun run() { for (field in fields.toList()) { // toList to avoid concurrent modification issues try { field.update() @@ -75,6 +68,10 @@ class TunerManager : Orchestrable, KoinComponent { } } + override suspend fun destroy() { + reset() + } + fun reset() { fields.clear() refreshFields() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt index a87d595c..85c78785 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt @@ -33,16 +33,14 @@ import io.github.classgraph.ClassGraph import io.github.deltacv.common.util.loggerForThis import org.firstinspires.ftc.vision.VisionProcessor import org.koin.core.component.KoinComponent -import org.koin.core.component.inject -import org.koin.core.qualifier.named import org.openftc.easyopencv.OpenCvPipeline -class AutoClasspathScan : ClasspathScan(), Orchestrable, KoinComponent { - val initOrchestrator: Orchestrator by inject(named("init")) - - init { - initOrchestrator.register(this) { - target { it.scan() } +class InitClasspathScan : ClasspathScan(), Orchestrable, KoinComponent { + override fun wire(orchestrator: Orchestrator) { + orchestrator.register(this) { + phase(Orchestrator.Phase.INIT) { + target { scan() } + } } } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt index a3aaec52..5fcb31b4 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt @@ -26,11 +26,10 @@ package com.github.serivesmejia.eocvsim.workspace import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.pipeline.PipelineManager -import com.github.serivesmejia.eocvsim.pipeline.compiler.CompiledPipelineManager +import com.github.serivesmejia.eocvsim.pipeline.compiled.CompiledPipelineManager import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler -import com.github.serivesmejia.eocvsim.util.event.Orchestrable -import com.github.serivesmejia.eocvsim.util.event.Orchestrator +import com.github.serivesmejia.eocvsim.util.event.MagicPhaseOrchestrable import com.github.serivesmejia.eocvsim.util.io.FileWatcher import com.github.serivesmejia.eocvsim.workspace.config.WorkspaceConfig import com.github.serivesmejia.eocvsim.workspace.config.WorkspaceConfigLoader @@ -62,15 +61,14 @@ import java.nio.file.Paths * classloader. */ @OptIn(DelicateCoroutinesApi::class) -class WorkspaceManager : Orchestrable, KoinComponent { +class WorkspaceManager : MagicPhaseOrchestrable(), KoinComponent { private val pipelineManager: PipelineManager by inject() private val compiledPipelineManager: CompiledPipelineManager by inject() - private val configManager: ConfigManager by inject() + private val configManager: ConfigManager by initDependency(inject()) private val params: EOCVSim.Parameters by inject() private val scope: CoroutineScope by inject() - private val initOrchestrator: Orchestrator by inject(named("init")) private val onMainLoop: EventHandler by inject(named("onMainLoop")) val logger by loggerForThis() @@ -216,29 +214,13 @@ class WorkspaceManager : Orchestrable, KoinComponent { lateinit var fileWatcher: FileWatcher private set - init { - initOrchestrator.register(this) { - target { it.init() } - dependsOn(configManager) - } - } - - /** - * Stops the current file watcher, if initialized - */ - fun stopFileWatcher() { - if (::fileWatcher.isInitialized) { - fileWatcher.stop() - } - } - /** * Initializes the workspace manager * To be called by the EOCVSim instance * Initializes the file watcher and the workspace configuration loader * and sets the workspace file to the initial workspace or the default one */ - private fun init() { + override suspend fun init() { onWorkspaceChange { fileWatcher.onChange { pipelineManager.compiledPipelineManager.asyncBuild() @@ -253,6 +235,23 @@ class WorkspaceManager : Orchestrable, KoinComponent { CompiledPipelineManager.DEF_WORKSPACE_FOLDER } + override suspend fun run() { + // TODO: check if we need to do anything here + } + + override suspend fun destroy() { + stopFileWatcher() + } + + /** + * Stops the current file watcher, if initialized + */ + fun stopFileWatcher() { + if (::fileWatcher.isInitialized) { + fileWatcher.stop() + } + } + /** * Creates a new workspace with the specified folder * from a template file bundled with the simulator 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 8d16ba7c..b8aebd8e 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 @@ -24,12 +24,15 @@ package io.github.deltacv.eocvsim.plugin.loader import com.github.serivesmejia.eocvsim.Build -import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.LifecycleSignal +import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.gui.DialogFactory +import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.gui.dialog.PluginOutput import com.github.serivesmejia.eocvsim.gui.dialog.PluginOutput.Companion.trimSpecials import com.github.serivesmejia.eocvsim.plugin.api.impl.EOCVSimApiImpl +import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.util.event.MagicPhaseOrchestrable import io.github.deltacv.common.util.loggerForThis import io.github.deltacv.common.util.loggerOf import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin @@ -37,31 +40,24 @@ import io.github.deltacv.eocvsim.plugin.repository.PluginRepositoryManager import io.github.deltacv.eocvsim.plugin.security.superaccess.SuperAccessDaemon import io.github.deltacv.eocvsim.plugin.security.superaccess.SuperAccessDaemonClient import io.github.deltacv.eocvsim.plugin.security.toMutable +import kotlinx.coroutines.channels.Channel +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named import java.io.File import java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock import kotlin.properties.Delegates -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject -import com.github.serivesmejia.eocvsim.config.ConfigManager -import com.github.serivesmejia.eocvsim.gui.Visualizer -import com.github.serivesmejia.eocvsim.util.event.EventHandler -import com.github.serivesmejia.eocvsim.util.event.Orchestrable -import com.github.serivesmejia.eocvsim.util.event.Orchestrator -import kotlinx.coroutines.channels.Channel -import org.koin.core.qualifier.named - /** * Manages the loading, enabling and disabling of plugins */ -class PluginManager : Orchestrable, KoinComponent { +class PluginManager : MagicPhaseOrchestrable(), KoinComponent { - private val configManager: ConfigManager by inject() + private val configManager: ConfigManager by initDependency(inject()) private val visualizer: Visualizer by inject() private val dialogFactory: DialogFactory by inject() - private val initOrchestrator: Orchestrator by inject(named("init")) private val onMainUpdate: EventHandler by inject(named("onMainLoop")) private val lifecycleChannel: Channel by inject(named("lifecycle")) @@ -133,13 +129,6 @@ class PluginManager : Orchestrable, KoinComponent { */ val eocvSimApiProvider = EOCVSimApiProvider { plugin -> EOCVSimApiImpl(plugin) } - init { - initOrchestrator.register(this) { - target { it.init() } - dependsOn(configManager) - } - } - /** * Initializes the plugin manager * Loads all plugin files in the plugins folder @@ -147,7 +136,7 @@ class PluginManager : Orchestrable, KoinComponent { * and stores them in the loaders map * @see PluginLoader */ - private fun init() { + override suspend fun init() { visualizer.onInitFinished { appender.append(PluginOutput.SPECIAL_FREE) } @@ -271,6 +260,12 @@ class PluginManager : Orchestrable, KoinComponent { loadPlugins() } + override suspend fun run() { } + + override suspend fun destroy() { + disablePlugins() + } + private fun addEmbeddedPlugin( pluginInfo: PluginInfo, pluginClass: Class From 35f3a55fdf4b136c51c7026cf732b741fa6fe237 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Mon, 27 Apr 2026 12:47:22 -0600 Subject: [PATCH 10/40] Support non-blocking cancellation of input sources --- .../serivesmejia/eocvsim/gui/DialogFactory.kt | 2 +- .../eocvsim/input/InputSourceInitializer.kt | 111 +++++++++++++----- .../eocvsim/input/InputSourceManager.kt | 18 ++- .../eocvsim/input/source/HttpSource.kt | 2 +- .../startup/StartupOrchestratorTest.kt | 0 5 files changed, 98 insertions(+), 35 deletions(-) delete mode 100644 EOCV-Sim/src/test/java/com/github/serivesmejia/eocvsim/startup/StartupOrchestratorTest.kt diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt index 560c8b29..81761106 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt @@ -85,7 +85,7 @@ class DialogFactory : KoinComponent { if (submessage != null) add(JLabel(submessage)) } - val options = if (cancelText != null) arrayOf(cancelText) else emptyArray() + val options = if (cancelText != null) arrayOf(cancelText) else null val pane = JOptionPane(panel, JOptionPane.INFORMATION_MESSAGE, JOptionPane.DEFAULT_OPTION, null, options) val dialog = pane.createDialog(parent, title) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt index f78b8c18..43aa0d2d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt @@ -1,9 +1,11 @@ package com.github.serivesmejia.eocvsim.input import io.github.deltacv.common.util.loggerForThis -import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeout @@ -13,19 +15,21 @@ import org.koin.core.context.GlobalContext class InputSourceInitializer : KoinComponent { + enum class Result { SUCCESS, FAILED, CANCELED, TIMED_OUT } + companion object { const val TIMEOUT = 10000L - fun runWithTimeout(sourceName: String, manager: InputSourceManager? = null, callback: () -> Boolean): Boolean { + fun runWithTimeout(sourceName: String, manager: InputSourceManager? = null, callback: () -> Boolean): Result { val initializer = GlobalContext.get().get() return initializer.runWithTimeout(sourceName, manager, callback) } - fun initializeWithTimeout(inputSource: InputSource): Boolean { + fun initializeWithTimeout(inputSource: InputSource, manager: InputSourceManager? = null): Result { val initializer = GlobalContext.get().get() - return initializer.initializeWithTimeout(inputSource) + return initializer.initializeWithTimeout(inputSource, manager) } } @@ -34,26 +38,57 @@ class InputSourceInitializer : KoinComponent { val logger by loggerForThis() @OptIn(DelicateCoroutinesApi::class) - fun initializeWithTimeout(inputSource: InputSource): Boolean { - var result = false + fun initializeWithTimeout(inputSource: InputSource, manager: InputSourceManager? = null): Result { + val resultSignal = CompletableDeferred() + val cancelSignal = Job().apply { + invokeOnCompletion { + if (!resultSignal.isCompleted) { + resultSignal.complete(Result.CANCELED) + } + } + } - val job = scope.launch { + scope.launch { try { - result = inputSource.init() + val initialized = inputSource.init() + + if (cancelSignal.isCancelled) { + runCatching { inputSource.close() } + .onFailure { logger.error("Error while closing canceled InputSource", it) } + + if (!resultSignal.isCompleted) { + resultSignal.complete(Result.CANCELED) + } + return@launch + } + + resultSignal.complete(if (initialized) Result.SUCCESS else Result.FAILED) } catch (e: Exception) { logger.error("Error initializing InputSource", e) + + if (cancelSignal.isCancelled) { + runCatching { inputSource.close() } + .onFailure { logger.error("Error while closing canceled InputSource", it) } + + if (!resultSignal.isCompleted) { + resultSignal.complete(Result.CANCELED) + } + } else { + resultSignal.complete(Result.FAILED) + } } } - runBlocking { + val dialog = manager?.showLoadingDialogIfNeeded(inputSource.name, cancelSignal) + + val result = runBlocking { try { - withTimeout(TIMEOUT) { - job.join() - } - } catch (e: CancellationException) { - logger.error("InputSource initialization timed out after $TIMEOUT ms", e) + withTimeout(TIMEOUT) { resultSignal.await() } + } catch (e: TimeoutCancellationException) { + Result.TIMED_OUT } finally { - job.cancel() + cancelSignal.cancel() + dialog?.dispose() } } @@ -62,28 +97,50 @@ class InputSourceInitializer : KoinComponent { @OptIn(DelicateCoroutinesApi::class) @JvmOverloads - fun runWithTimeout(sourceName: String, manager: InputSourceManager? = null, callback: () -> Boolean): Boolean { - var result = false + fun runWithTimeout(sourceName: String, manager: InputSourceManager? = null, callback: () -> Boolean): Result { + val resultSignal = CompletableDeferred() + val cancelSignal = Job().apply { + invokeOnCompletion { + if (!resultSignal.isCompleted) { + resultSignal.complete(Result.CANCELED) + } + } + } - val job = scope.launch { + scope.launch { try { - result = callback() + val ran = callback() + + if (cancelSignal.isCancelled) { + if (!resultSignal.isCompleted) { + resultSignal.complete(Result.CANCELED) + } + return@launch + } + + resultSignal.complete(if (ran) Result.SUCCESS else Result.FAILED) } catch (e: Exception) { logger.error("Error running InputSource", e) + + if (cancelSignal.isCancelled) { + if (!resultSignal.isCompleted) { + resultSignal.complete(Result.CANCELED) + } + } else { + resultSignal.complete(Result.FAILED) + } } } - val dialog = manager?.showLoadingDialogIfNeeded(sourceName, job) + val dialog = manager?.showLoadingDialogIfNeeded(sourceName, cancelSignal) - runBlocking { + val result = runBlocking { try { - withTimeout(TIMEOUT) { - job.join() - } - } catch (e: CancellationException) { - logger.error("InputSource run timed out after $TIMEOUT ms", e) + withTimeout(TIMEOUT) { resultSignal.await() } + } catch (e: TimeoutCancellationException) { + Result.TIMED_OUT } finally { - job.cancel() + cancelSignal.cancel() dialog?.dispose() } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt index dd9a322d..02d33cc8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt @@ -232,12 +232,18 @@ class InputSourceManager : MagicPhaseOrchestrable(), KoinComponent { src?.reset() if (src != null) { - if (!inputSourceInitializer.initializeWithTimeout(src)) { - onInputSourceInitError.run() - - logger.error("Error while loading requested source ($sourceName) reported by itself (init method returned false)") - - return false + when (val initResult = inputSourceInitializer.initializeWithTimeout(src, this)) { + InputSourceInitializer.Result.SUCCESS -> Unit + InputSourceInitializer.Result.CANCELED -> { + logger.info("Input source loading canceled by user ($sourceName)") + return false + } + InputSourceInitializer.Result.FAILED, + InputSourceInitializer.Result.TIMED_OUT -> { + onInputSourceInitError.run() + logger.error("Error while loading requested source ($sourceName), result=$initResult") + return false + } } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt index 860a8e5a..f2b1bb8b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt @@ -91,7 +91,7 @@ class HttpSource @JvmOverloads constructor( frame != null } - if (!result || frame == null) { + if (result != InputSourceInitializer.Result.SUCCESS || frame == null) { return null } diff --git a/EOCV-Sim/src/test/java/com/github/serivesmejia/eocvsim/startup/StartupOrchestratorTest.kt b/EOCV-Sim/src/test/java/com/github/serivesmejia/eocvsim/startup/StartupOrchestratorTest.kt deleted file mode 100644 index e69de29b..00000000 From 03d26c5aca452d8617618cdf862bc2034642fa2d Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 28 Apr 2026 10:27:38 -0600 Subject: [PATCH 11/40] Reshape PhaseOrchestrable with external PhaseDependencies component & prompt for restart in theme change --- .../eocvsim/util/event/EventHandler.kt | 263 ++++++++++-------- .../util/orchestration/Orchestrable.kt | 200 +++++++++++++ .../{event => orchestration}/Orchestrator.kt | 146 +--------- .../github/serivesmejia/eocvsim/EOCVSim.kt | 2 +- .../com/github/serivesmejia/eocvsim/Module.kt | 4 +- .../eocvsim/config/ConfigManager.kt | 5 +- .../serivesmejia/eocvsim/gui/Visualizer.kt | 52 +++- .../eocvsim/gui/dialog/Configuration.kt | 11 +- .../serivesmejia/eocvsim/input/InputSource.kt | 5 + .../eocvsim/input/InputSourceInitializer.kt | 61 ++-- .../eocvsim/input/InputSourceManager.kt | 29 +- .../eocvsim/input/source/CameraSource.kt | 9 +- .../eocvsim/input/source/HttpSource.kt | 8 +- .../eocvsim/input/source/VideoSource.kt | 2 + .../eocvsim/pipeline/PipelineManager.kt | 86 +++--- .../compiled/CompiledPipelineManager.kt | 9 +- .../eocvsim/tuner/TunerManager.kt | 10 +- .../tuner/field/numeric/DoubleField.kt | 3 +- .../eocvsim/util/ClasspathScan.kt | 7 +- .../util/exception/handling/CrashReport.kt | 5 +- .../eocvsim/workspace/WorkspaceManager.kt | 7 +- .../eocvsim/plugin/loader/PluginManager.kt | 7 +- 22 files changed, 516 insertions(+), 415 deletions(-) create mode 100644 Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrable.kt rename Common/src/main/java/com/github/serivesmejia/eocvsim/util/{event => orchestration}/Orchestrator.kt (74%) diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventHandler.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventHandler.kt index bd669cc1..f923cc82 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventHandler.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventHandler.kt @@ -1,76 +1,67 @@ package com.github.serivesmejia.eocvsim.util.event import io.github.deltacv.common.util.loggerOf -import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.job import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import java.util.concurrent.atomic.AtomicInteger /** - * Event handler with: - * - Persistent listeners (ID-based, queue-based adding) - * - Once listeners (double buffered queue-based, deferred) + * Generic parameterized event handler. Listeners receive a payload of type T. + * This is a new implementation; the original zero-arg `EventHandler` is kept + * for compatibility and extends this class with `T = Unit`. */ -class EventHandler @JvmOverloads constructor( +typealias ParamOnceEventListener = (T) -> Unit +typealias ParamEventListener = ParamEventListenerContext.(T) -> Unit + +class ParamEventListenerContext( + private val handler: ParamEventHandler, + val id: EventListenerId, +) { + fun removeListener() { + handler.removeListener(id) + } +} + +open class ParamEventHandler @JvmOverloads constructor( val name: String, - var callRightAway: CallRightAway = CallRightAway.Disabled, + var callRightAway: EventHandler.CallRightAway = EventHandler.CallRightAway.Disabled, val catchExceptions: Boolean = true -) : Runnable { - - // ------------------------------------------------------------ - // config - // ------------------------------------------------------------ +) { private val logger by loggerOf("EventHandler-$name") - // ------------------------------------------------------------ // ids - // ------------------------------------------------------------ - - private val idCounter = AtomicInteger(Int.MIN_VALUE) - - // ------------------------------------------------------------ - // persistent listeners (stable + queues) - // ------------------------------------------------------------ - - private val persistentListeners = HashMap() - - private val persistentAddQueue = ArrayDeque>() - private val persistentRemoveQueue = ArrayDeque() - - private val persistentQueueLock = Any() - - private val persistentContextCache = HashMap() - - // ------------------------------------------------------------ - // once listeners (swappable double buffer) - // ------------------------------------------------------------ - - private var onceListenersCurrent = ArrayDeque() - private var onceIdsCurrent = ArrayDeque() - - private var onceListenersQueue = ArrayDeque() - private var onceIdsQueue = ArrayDeque() - - private val onceLock = Any() - - // ------------------------------------------------------------ - // run - // ------------------------------------------------------------ - - override fun run() { - runPersistentListeners() - runOnceListeners() + protected val idCounter = AtomicInteger(Int.MIN_VALUE) + + // persistent listeners + protected val persistentListeners = HashMap>() + protected val persistentAddQueue = ArrayDeque>>() + protected val persistentRemoveQueue = ArrayDeque() + protected val persistentQueueLock = Any() + protected val persistentContextCache = HashMap>() + + // once listeners + protected var onceListenersCurrent = ArrayDeque>() + protected var onceIdsCurrent = ArrayDeque() + + protected var onceListenersQueue = ArrayDeque>() + protected var onceIdsQueue = ArrayDeque() + protected val onceLock = Any() + + // run with payload + fun run(payload: T) { + runPersistentListeners(payload) + runOnceListeners(payload) } - // ------------------------------------------------------------ - // persistent execution - // ------------------------------------------------------------ + // public attach/once for payload listeners (named to avoid conflict with + // legacy zero-arg EventHandler overloads) + fun attachPayload(listener: ParamEventListener): EventListenerId = attachRaw(listener) - fun runPersistentListeners() { - // apply pending adds/removes + fun oncePayload(listener: ParamOnceEventListener): EventListenerId = onceRaw(listener) + + protected fun runPersistentListeners(payload: T) { synchronized(persistentQueueLock) { while (persistentAddQueue.isNotEmpty()) { val (id, listener) = persistentAddQueue.removeFirst() @@ -83,14 +74,13 @@ class EventHandler @JvmOverloads constructor( } } - fun run(id: Int, listener: EventListener) { - val remover = persistentContextCache.getOrPut(id) { EventListenerContext(this, EventListenerId(id)) } - listener(remover) + fun run(id: Int, listener: ParamEventListener) { + val remover = persistentContextCache.getOrPut(id) { ParamEventListenerContext(this, EventListenerId(id)) } + listener(remover, payload) } - // execute for ((id, listener) in persistentListeners) { - if(catchExceptions) { + if (catchExceptions) { try { run(id, listener) } catch (e: Exception) { @@ -103,16 +93,11 @@ class EventHandler @JvmOverloads constructor( } } - // ------------------------------------------------------------ - // once execution - // ------------------------------------------------------------ - - fun runOnceListeners() { - val toRunListeners: ArrayDeque + protected fun runOnceListeners(payload: T) { + val toRunListeners: ArrayDeque> val toRunIds: ArrayDeque synchronized(onceLock) { - // swap toRunListeners = onceListenersQueue toRunIds = onceIdsQueue @@ -125,17 +110,17 @@ class EventHandler @JvmOverloads constructor( while (toRunListeners.isNotEmpty()) { val listener = toRunListeners.removeFirst() - toRunIds.removeFirst() // keep in sync + toRunIds.removeFirst() - if(catchExceptions) { + if (catchExceptions) { try { - listener() + listener(payload) } catch (e: Exception) { if (e is InterruptedException) throw e logger.error("Exception in once listener", e) } } else { - listener() + listener(payload) } } @@ -143,81 +128,41 @@ class EventHandler @JvmOverloads constructor( toRunIds.clear() } - // ------------------------------------------------------------ - // attach - // ------------------------------------------------------------ - - operator fun invoke(listener: EventListener): EventListenerId = attach(listener) - - fun attach(listener: EventListener): EventListenerId { + // attach/once that don't implement callRightAway semantics; subclasses + // (like the legacy EventHandler) may implement immediate invocation. + protected fun attachRaw(listener: ParamEventListener): EventListenerId { val id = EventListenerId(idCounter.getAndIncrement()) - synchronized(persistentQueueLock) { persistentAddQueue.addLast(id.value to listener) } - - - when(val mode = callRightAway) { - CallRightAway.InPlace -> listener(EventListenerContext(this, id)) - is CallRightAway.InScope -> { - val job = mode.scope.launch { - listener(EventListenerContext(this@EventHandler, id)) - } - - if(mode.shouldJoin) { - runBlocking { job.join() } - } - } - CallRightAway.Disabled -> {} // do nothing - } - return id } - fun once(listener: OnceEventListener): EventListenerId { + protected fun onceRaw(listener: ParamOnceEventListener): EventListenerId { val id = EventListenerId(idCounter.getAndIncrement()) - when(val mode = callRightAway) { - CallRightAway.InPlace -> listener() - is CallRightAway.InScope -> { - val job = mode.scope.launch { - listener() - } - - if(mode.shouldJoin) { - runBlocking { job.join() } - } - } - CallRightAway.Disabled -> { - synchronized(onceLock) { - onceListenersQueue.addLast(listener) - onceIdsQueue.addLast(id.value) - } - } + // For the generic/payload handler we don't attempt to call listeners + // immediately since we don't have a payload value here. Always enqueue. + synchronized(onceLock) { + onceListenersQueue.addLast(listener) + onceIdsQueue.addLast(id.value) } return id } - @JvmName("attach") - fun attach(runnable: Runnable): EventListenerId = attach{ runnable.run() } + protected fun attachRunnable(runnable: Runnable): EventListenerId = attachRaw { runnable.run() } - @JvmName("once") - fun once(runnable: Runnable): EventListenerId = once { runnable.run() } + protected fun onceRunnable(runnable: Runnable): EventListenerId = onceRaw { runnable.run() } - // ------------------------------------------------------------ // remove - // ------------------------------------------------------------ - - @JvmName("removeListener") - fun removeListener(id: EventListenerId) { + open fun removeListener(id: EventListenerId) { synchronized(onceLock) { val index = onceIdsQueue.indexOf(id.value) if (index >= 0) { onceIdsQueue.removeAt(index) onceListenersQueue.removeAt(index) - - return@removeListener // removed from once queue, no need to check persistent + return } } @@ -240,6 +185,78 @@ class EventHandler @JvmOverloads constructor( onceIdsQueue.clear() } } +} + +/** + * Legacy zero-arg EventHandler kept for compatibility. It extends the + * parameterized implementation with T = Unit and provides the exact old API. + */ + +class EventHandler @JvmOverloads constructor( + name: String, + callRightAway: CallRightAway = CallRightAway.Disabled, + catchExceptions: Boolean = true +) : ParamEventHandler(name, callRightAway, catchExceptions), Runnable { + + private val logger by loggerOf("EventHandler-$name") + + override fun run() { + run(Unit) + } + + // ------------------------------------------------------------ + // attach (legacy API) + // ------------------------------------------------------------ + operator fun invoke(listener: EventListener): EventListenerId = attach(listener) + + fun attach(listener: EventListener): EventListenerId { + // wrapper adapts legacy EventListenerContext receiver to ParamEventListenerContext + val wrapper: ParamEventListener = { _ -> + // `this` is ParamEventListenerContext + val ctx = EventListenerContext(this@EventHandler, EventListenerId(this.id.value)) + listener(ctx) + } + + val id = attachRaw(wrapper) + + when (val mode = callRightAway) { + CallRightAway.InPlace -> listener(EventListenerContext(this, id)) + is CallRightAway.InScope -> { + val job = mode.scope.launch { + listener(EventListenerContext(this@EventHandler, id)) + } + if (mode.shouldJoin) runBlocking { job.join() } + } + CallRightAway.Disabled -> {} + } + + return id + } + + fun once(listener: OnceEventListener): EventListenerId { + val wrapper: ParamOnceEventListener = { _ -> listener() } + + val id = onceRaw(wrapper) + + when (val mode = callRightAway) { + CallRightAway.InPlace -> listener() + is CallRightAway.InScope -> { + val job = mode.scope.launch { listener() } + if (mode.shouldJoin) runBlocking { job.join() } + } + CallRightAway.Disabled -> {} + } + + return id + } + + @JvmName("attach") + fun attach(runnable: Runnable): EventListenerId = super.attachRunnable(runnable) + + @JvmName("once") + fun once(runnable: Runnable): EventListenerId = super.onceRunnable(runnable) + + // reuse removeListener from ParamEventHandler sealed interface CallRightAway { data class InScope(val scope: CoroutineScope, val shouldJoin: Boolean = false) : CallRightAway diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrable.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrable.kt new file mode 100644 index 00000000..53707d2b --- /dev/null +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrable.kt @@ -0,0 +1,200 @@ +@file:Suppress("unused") + +package com.github.serivesmejia.eocvsim.util.orchestration + +import kotlin.reflect.KClass + +interface Orchestrable { + fun wire(orchestrator: Orchestrator) +} + +class PhaseDependencies { + private val _initDependencies = mutableListOf() + private val _runDependencies = mutableListOf() + private val _destroyDependencies = mutableListOf() + + val initDependencies: List get() = _initDependencies + val runDependencies: List get() = _runDependencies + val destroyDependencies: List get() = _destroyDependencies + + /** + * @param phase if empty, will bind to all phases + */ + fun dependency(target: KClass, vararg phase: Orchestrator.Phase): KClass { + if (phase.isEmpty()) { + initDependency(target) + runDependency(target) + destroyDependency(target) + } else { + for (p in phase) { + when (p) { + Orchestrator.Phase.INIT -> initDependency(target) + Orchestrator.Phase.RUN -> runDependency(target) + Orchestrator.Phase.DESTROY -> destroyDependency(target) + } + } + } + + return target + } + + fun dependency(target: T, vararg phase: Orchestrator.Phase): T { + if (phase.isEmpty()) { + initDependency(target) + runDependency(target) + destroyDependency(target) + } else { + for (p in phase) { + when (p) { + Orchestrator.Phase.INIT -> initDependency(target) + Orchestrator.Phase.RUN -> runDependency(target) + Orchestrator.Phase.DESTROY -> destroyDependency(target) + } + } + } + + return target + } + + inline fun dependency(target: Lazy, vararg phase: Orchestrator.Phase): Lazy { + dependency(T::class, *phase) + return target + } + + // -- INIT DEPENDENCY SUGAR -- + + fun initDependency(target: KClass): KClass { + _initDependencies.add(Orchestrator.Dependency.Class(target)) + return target + } + + fun initDependency(target: T): T { + _initDependencies.add(Orchestrator.Dependency.Instance(target)) + return target + } + + inline fun initDependency(target: Lazy): Lazy { + initDependency(T::class) + return target + } + + // -- RUN DEPENDENCY SUGAR -- + + fun runDependency(target: KClass): KClass { + _runDependencies.add(Orchestrator.Dependency.Class(target)) + return target + } + + fun runDependency(target: T): T { + _runDependencies.add(Orchestrator.Dependency.Instance(target)) + return target + } + + inline fun runDependency(target: Lazy): Lazy { + runDependency(T::class) + return target + } + + // -- DESTROY DEPENDENCY SUGAR -- + + fun destroyDependency(target: KClass): KClass { + _destroyDependencies.add(Orchestrator.Dependency.Class(target)) + return target + } + + fun destroyDependency(target: T): T { + _destroyDependencies.add(Orchestrator.Dependency.Instance(target)) + return target + } + + inline fun destroyDependency(target: Lazy): Lazy { + destroyDependency(T::class) + return target + } +} + +interface PhaseOrchestrable : Orchestrable { + val phaseDependencies: PhaseDependencies + + suspend fun init() + suspend fun run() + suspend fun destroy() + + override fun wire(orchestrator: Orchestrator) { + orchestrator.register(this) { + phase(Orchestrator.Phase.INIT) { + target { init() } + dependsOn(*phaseDependencies.initDependencies.toTypedArray()) + } + + phase(Orchestrator.Phase.RUN) { + target { run() } + dependsOn(*phaseDependencies.runDependencies.toTypedArray()) + } + + phase(Orchestrator.Phase.DESTROY) { + target { destroy() } + dependsOn(*phaseDependencies.destroyDependencies.toTypedArray()) + } + } + } +} + +/** + * Dependency helper extensions for any [PhaseOrchestrable]. + */ + +/** + * @param phase if empty, will bind to all phases + */ +fun PhaseOrchestrable.dependency(target: KClass, vararg phase: Orchestrator.Phase): KClass { + phaseDependencies.dependency(target, *phase) + return target +} + +fun PhaseOrchestrable.dependency(target: T, vararg phase: Orchestrator.Phase): T { + phaseDependencies.dependency(target, *phase) + return target +} + +inline fun PhaseOrchestrable.dependency(target: Lazy, vararg phase: Orchestrator.Phase): Lazy { + phaseDependencies.dependency(T::class, *phase) + return target +} + +// -- INIT DEPENDENCY SUGAR -- + +fun PhaseOrchestrable.initDependency(target: KClass): KClass = phaseDependencies.initDependency(target) + +fun PhaseOrchestrable.initDependency(target: T): T = phaseDependencies.initDependency(target) + +inline fun PhaseOrchestrable.initDependency(target: Lazy): Lazy { + phaseDependencies.initDependency(T::class) + return target +} + +// -- RUN DEPENDENCY SUGAR -- + +fun PhaseOrchestrable.runDependency(target: KClass): KClass = phaseDependencies.runDependency(target) + +fun PhaseOrchestrable.runDependency(target: T): T = phaseDependencies.runDependency(target) + +inline fun PhaseOrchestrable.runDependency(target: Lazy): Lazy { + phaseDependencies.runDependency(T::class) + return target +} + +// -- DESTROY DEPENDENCY SUGAR -- + +fun PhaseOrchestrable.destroyDependency(target: KClass): KClass = phaseDependencies.destroyDependency(target) + +fun PhaseOrchestrable.destroyDependency(target: T): T = phaseDependencies.destroyDependency(target) + +inline fun PhaseOrchestrable.destroyDependency(target: Lazy): Lazy { + phaseDependencies.destroyDependency(T::class) + return target +} + +abstract class PhaseOrchestrableBase : PhaseOrchestrable { + final override val phaseDependencies: PhaseDependencies = PhaseDependencies() +} \ No newline at end of file diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/Orchestrator.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrator.kt similarity index 74% rename from Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/Orchestrator.kt rename to Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrator.kt index 06942b9a..d3f85e4e 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/Orchestrator.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrator.kt @@ -1,6 +1,6 @@ @file:Suppress("unused") -package com.github.serivesmejia.eocvsim.util.event +package com.github.serivesmejia.eocvsim.util.orchestration import io.github.deltacv.common.util.loggerOf import kotlinx.coroutines.* @@ -446,147 +446,3 @@ class Orchestrator( } } } -interface Orchestrable { - fun wire(orchestrator: Orchestrator) -} - -interface PhaseOrchestrable : Orchestrable { - val initDependencies: List - get() = emptyList() - - val runDependencies: List - get() = emptyList() - - val destroyDependencies: List - get() = emptyList() - - suspend fun init() - suspend fun run() - suspend fun destroy() - - override fun wire(orchestrator: Orchestrator) { - orchestrator.register(this) { - phase(Orchestrator.Phase.INIT) { - target { init() } - dependsOn(*initDependencies.toTypedArray()) - } - - phase(Orchestrator.Phase.RUN) { - target { run() } - dependsOn(*runDependencies.toTypedArray()) - } - - phase(Orchestrator.Phase.DESTROY) { - target { destroy() } - dependsOn(*destroyDependencies.toTypedArray()) - } - } - } -} - -abstract class MagicPhaseOrchestrable : PhaseOrchestrable { - - private val _initDependencies = mutableListOf() - override val initDependencies = _initDependencies - - private val _runDependencies = mutableListOf() - override val runDependencies = _runDependencies - - private val _destroyDependencies = mutableListOf() - override val destroyDependencies = _destroyDependencies - - /** - * @param phase if empty, will bind to all phases - */ - fun dependency(target: KClass, vararg phase: Orchestrator.Phase): KClass { - if(phase.isEmpty()) { - initDependency(target) - runDependency(target) - destroyDependency(target) - } else { - for (p in phase) { - when (p) { - Orchestrator.Phase.INIT -> initDependency(target) - Orchestrator.Phase.RUN -> runDependency(target) - Orchestrator.Phase.DESTROY -> destroyDependency(target) - } - } - } - - return target - } - - fun dependency(target: T, vararg phase: Orchestrator.Phase): T { - if(phase.isEmpty()) { - initDependency(target) - runDependency(target) - destroyDependency(target) - } else { - for (p in phase) { - when (p) { - Orchestrator.Phase.INIT -> initDependency(target) - Orchestrator.Phase.RUN -> runDependency(target) - Orchestrator.Phase.DESTROY -> destroyDependency(target) - } - } - } - - return target - } - - inline fun dependency(target: Lazy, vararg phase: Orchestrator.Phase): Lazy { - dependency(T::class, *phase) - return target - } - - // -- INIT DEPENDENCY SUGAR -- - - fun initDependency(target: KClass): KClass { - initDependencies.add(Orchestrator.Dependency.Class(target)) - return target - } - - fun initDependency(target: T): T { - initDependencies.add(Orchestrator.Dependency.Instance(target)) - return target - } - - inline fun initDependency(target: Lazy): Lazy { - initDependency(T::class) - return target - } - - // -- RUN DEPENDENCY SUGAR -- - - fun runDependency(target: KClass): KClass { - runDependencies.add(Orchestrator.Dependency.Class(target)) - return target - } - - fun runDependency(target: T): T { - runDependencies.add(Orchestrator.Dependency.Instance(target)) - return target - } - - inline fun runDependency(target: Lazy): Lazy { - runDependency(T::class) - return target - } - - // -- DESTROY DEPENDENCY SUGAR -- - - fun destroyDependency(target: KClass): KClass { - destroyDependencies.add(Orchestrator.Dependency.Class(target)) - return target - } - - fun destroyDependency(target: T): T { - destroyDependencies.add(Orchestrator.Dependency.Instance(target)) - return target - } - - inline fun destroyDependency(target: Lazy): Lazy { - destroyDependency(T::class) - return target - } -} \ No newline at end of file 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 a51524a4..5f6dca89 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 @@ -34,7 +34,7 @@ import com.github.serivesmejia.eocvsim.pipeline.PipelineSource import com.github.serivesmejia.eocvsim.tuner.TunerManager import com.github.serivesmejia.eocvsim.util.JavaProcess import com.github.serivesmejia.eocvsim.util.event.EventHandler -import com.github.serivesmejia.eocvsim.util.event.Orchestrator +import com.github.serivesmejia.eocvsim.util.orchestration.Orchestrator import com.github.serivesmejia.eocvsim.util.exception.handling.CrashReport import com.github.serivesmejia.eocvsim.util.exception.handling.EOCVSimUncaughtExceptionHandler import com.github.serivesmejia.eocvsim.util.fps.FpsLimiter diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt index cde10972..2b486190 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt @@ -11,8 +11,8 @@ import com.github.serivesmejia.eocvsim.pipeline.compiled.CompiledPipelineManager import com.github.serivesmejia.eocvsim.tuner.TunerManager import com.github.serivesmejia.eocvsim.util.InitClasspathScan import com.github.serivesmejia.eocvsim.util.event.EventHandler -import com.github.serivesmejia.eocvsim.util.event.Orchestrable -import com.github.serivesmejia.eocvsim.util.event.Orchestrator +import com.github.serivesmejia.eocvsim.util.orchestration.Orchestrable +import com.github.serivesmejia.eocvsim.util.orchestration.Orchestrator import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator import io.github.deltacv.eocvsim.plugin.loader.PluginManager diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt index dc2e8e1c..ab74e8e8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt @@ -23,12 +23,11 @@ package com.github.serivesmejia.eocvsim.config -import com.github.serivesmejia.eocvsim.util.event.MagicPhaseOrchestrable -import com.github.serivesmejia.eocvsim.util.event.PhaseOrchestrable +import com.github.serivesmejia.eocvsim.util.orchestration.PhaseOrchestrableBase import io.github.deltacv.common.util.loggerForThis import org.koin.core.component.KoinComponent -class ConfigManager : MagicPhaseOrchestrable(), KoinComponent { +class ConfigManager : PhaseOrchestrableBase(), KoinComponent { val configLoader = ConfigLoader() var config: Config = Config() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt index 8100d6fd..889eae02 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt @@ -44,8 +44,8 @@ import com.github.serivesmejia.eocvsim.output.RecordingManager import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.pipeline.compiled.PipelineCompiler import com.github.serivesmejia.eocvsim.util.event.EventHandler -import com.github.serivesmejia.eocvsim.util.event.MagicPhaseOrchestrable -import com.github.serivesmejia.eocvsim.util.event.Orchestrator +import com.github.serivesmejia.eocvsim.util.orchestration.initDependency +import com.github.serivesmejia.eocvsim.util.orchestration.PhaseOrchestrableBase import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager import com.github.serivesmejia.eocvsim.workspace.util.VSCodeLauncher import com.github.serivesmejia.eocvsim.workspace.util.template.GradleWorkspaceTemplate @@ -58,12 +58,14 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.swing.Swing import kotlinx.coroutines.withContext +import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.koin.core.qualifier.named import org.opencv.core.Size import org.openftc.easyopencv.OpenCvViewport import java.awt.BorderLayout +import java.util.concurrent.CancellationException import java.awt.Dimension import java.awt.Taskbar import java.awt.event.MouseAdapter @@ -72,7 +74,7 @@ import java.awt.event.WindowAdapter import java.awt.event.WindowEvent import javax.swing.* -class Visualizer : MagicPhaseOrchestrable(), KoinComponent { +class Visualizer : PhaseOrchestrableBase(), KoinComponent { val onMainUpdate: EventHandler by inject(named("onMainLoop")) @@ -87,8 +89,6 @@ class Visualizer : MagicPhaseOrchestrable(), KoinComponent { val pipelineStatisticsCalculator: PipelineStatisticsCalculator by inject() val scope: CoroutineScope by inject() - private val initOrchestrator: Orchestrator by inject(named("init")) - val onInitFinished = EventHandler("OnVisualizerInitFinish") val onPluginGuiAttachment = EventHandler("OnPluginGuiAttachment") @@ -236,9 +236,9 @@ class Visualizer : MagicPhaseOrchestrable(), KoinComponent { val taskbar = Taskbar.getTaskbar() try { taskbar.iconImage = EOCVSimIconLibrary.icoEOCVSim128.image - } catch (e: UnsupportedOperationException) { + } catch (_: UnsupportedOperationException) { logger.warn("Setting the Taskbar icon image is not supported on this platform") - } catch (e: SecurityException) { + } catch (_: SecurityException) { logger.warn("Setting the Taskbar icon image was not allowed by the security manager") } } @@ -261,6 +261,38 @@ class Visualizer : MagicPhaseOrchestrable(), KoinComponent { ) } + // Subscribe to initializer begin events so the UI can show a cancelable loading + // dialog for slow initializations without the initializer knowing about the UI. + val inputSourceInitializer: com.github.serivesmejia.eocvsim.input.InputSourceInitializer by inject() + inputSourceInitializer.onInitBegin.attachPayload { session -> + // Let listeners decide whether to show a dialog. We show it when the + // session explicitly requested a dialog or when the source itself + // signals a slow initialization. + val shouldShow = session.hasSlowInitialization || (session.inputSource?.hasSlowInitialization == true) + + if (!shouldShow) return@attachPayload + + val dialog = dialogFactory.createInformation( + frame, + if (session.sourceName.isNullOrBlank()) "Opening source..." else "Opening ${session.sourceName}...", + null, + "Information", + "Cancel" + ) { + session.cancelJob.cancel(CancellationException()) + } + + // Dispose dialog when the session finishes (success/failure/cancel/timeout) + scope.launch { + try { + session.resultSignal.await() + } catch (_: Exception) { + } finally { + dialog.dispose() + } + } + } + hasFinishedInitializing = true if (!PipelineCompiler.IS_USABLE) { @@ -308,12 +340,6 @@ class Visualizer : MagicPhaseOrchestrable(), KoinComponent { frame.title = "$title - $titleMsg" } - fun setTitle(title: String) { - this.title = title - if (beforeTitle != title) updateFrameTitle(title, titleMsg) - beforeTitle = title - } - fun setTitleMessage(titleMsg: String) { this.titleMsg = titleMsg if (beforeTitleMsg != titleMsg) updateFrameTitle(title, titleMsg) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.kt index 730f67d8..b7fc1de0 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.kt @@ -24,8 +24,10 @@ package com.github.serivesmejia.eocvsim.gui.dialog import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.LifecycleSignal import com.github.serivesmejia.eocvsim.config.Config import com.github.serivesmejia.eocvsim.config.ConfigManager +import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.gui.component.input.EnumComboBox import com.github.serivesmejia.eocvsim.gui.component.input.SizeFields @@ -34,6 +36,7 @@ import com.github.serivesmejia.eocvsim.gui.util.WebcamDriver import com.github.serivesmejia.eocvsim.pipeline.PipelineFps import com.github.serivesmejia.eocvsim.pipeline.PipelineTimeout import com.github.serivesmejia.eocvsim.util.event.EventHandler +import kotlinx.coroutines.channels.Channel import java.awt.FlowLayout import java.awt.GridBagConstraints import java.awt.GridBagLayout @@ -47,6 +50,8 @@ import org.koin.core.qualifier.named class Configuration : KoinComponent { private val visualizer: Visualizer by inject() + private val lifecycle: Channel by inject(named("lifecycle")) + private val dialogFactory: DialogFactory by inject() private val configManager: ConfigManager by inject() private val onMainLoop: EventHandler by inject(named("onMainLoop")) @@ -54,7 +59,6 @@ class Configuration : KoinComponent { private val dialog = JDialog(visualizer.frame) - private val themeComboBox: JComboBox private val superAccessCheckBox: JCheckBox private val prefersPaperVisionCheckbox: JCheckBox @@ -196,8 +200,9 @@ class Configuration : KoinComponent { configManager.saveToFile() if (userSelectedTheme != previousTheme) { - // TODO: Implement restart() - // eocvSim.restart() + dialogFactory.createYesOrNo(dialog, "Applying a new interface theme requires restarting.", "Do you wish to restart now?") { + lifecycle.trySend(LifecycleSignal.Restart) + } } } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt index edf8b565..880c2f0a 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt @@ -31,6 +31,11 @@ import javax.swing.filechooser.FileFilter abstract class InputSource : Comparable, KoinComponent { + // Computed property (no backing field) to avoid adding serializable fields that + // cause duplicate JSON field errors with Gson when subclasses also declare + // a backing field. Subclasses should override with a getter as well. + open val hasSlowInitialization: Boolean get() = false + @Transient var isDefault = false @Transient open var name: String = "" diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt index 43aa0d2d..69a9f101 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt @@ -1,17 +1,12 @@ package com.github.serivesmejia.eocvsim.input +import com.github.serivesmejia.eocvsim.util.event.ParamEventHandler import io.github.deltacv.common.util.loggerForThis -import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.Job -import kotlinx.coroutines.TimeoutCancellationException -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withTimeout +import kotlinx.coroutines.* import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.koin.core.context.GlobalContext +import java.util.concurrent.atomic.AtomicInteger class InputSourceInitializer : KoinComponent { @@ -20,16 +15,19 @@ class InputSourceInitializer : KoinComponent { companion object { const val TIMEOUT = 10000L - fun runWithTimeout(sourceName: String, manager: InputSourceManager? = null, callback: () -> Boolean): Result { + fun runWithTimeout(sourceName: String, callback: () -> Boolean): Result { + val initializer = GlobalContext.get().get() + return initializer.runWithTimeout(sourceName, false, callback) + } + fun runWithTimeout(inputSource: InputSource, callback: () -> Boolean): Result { val initializer = GlobalContext.get().get() - return initializer.runWithTimeout(sourceName, manager, callback) + return initializer.runWithTimeout(inputSource.name, inputSource.hasSlowInitialization, callback) } - fun initializeWithTimeout(inputSource: InputSource, manager: InputSourceManager? = null): Result { - + fun initializeWithTimeout(inputSource: InputSource): Result { val initializer = GlobalContext.get().get() - return initializer.initializeWithTimeout(inputSource, manager) + return initializer.initializeWithTimeout(inputSource) } } @@ -37,8 +35,21 @@ class InputSourceInitializer : KoinComponent { val logger by loggerForThis() - @OptIn(DelicateCoroutinesApi::class) - fun initializeWithTimeout(inputSource: InputSource, manager: InputSourceManager? = null): Result { + // Event fired when an initialization that may need UI interaction starts. + // Listeners will receive the InitSession payload directly. + val onInitBegin = ParamEventHandler("InputSourceInitBegin") + + private val sessionIdCounter = AtomicInteger(0) + data class InitSession( + val id: Int, + val inputSource: InputSource?, + val sourceName: String?, + val cancelJob: Job, + val resultSignal: CompletableDeferred, + val hasSlowInitialization: Boolean = false + ) + + fun initializeWithTimeout(inputSource: InputSource): Result { val resultSignal = CompletableDeferred() val cancelSignal = Job().apply { invokeOnCompletion { @@ -48,6 +59,9 @@ class InputSourceInitializer : KoinComponent { } } + val sessionId = sessionIdCounter.getAndIncrement() + val session = InitSession(sessionId, inputSource, inputSource.name, cancelSignal, resultSignal, inputSource.hasSlowInitialization) + scope.launch { try { val initialized = inputSource.init() @@ -79,16 +93,15 @@ class InputSourceInitializer : KoinComponent { } } - val dialog = manager?.showLoadingDialogIfNeeded(inputSource.name, cancelSignal) + onInitBegin.run(session) val result = runBlocking { try { withTimeout(TIMEOUT) { resultSignal.await() } - } catch (e: TimeoutCancellationException) { + } catch (_: TimeoutCancellationException) { Result.TIMED_OUT } finally { cancelSignal.cancel() - dialog?.dispose() } } @@ -97,7 +110,7 @@ class InputSourceInitializer : KoinComponent { @OptIn(DelicateCoroutinesApi::class) @JvmOverloads - fun runWithTimeout(sourceName: String, manager: InputSourceManager? = null, callback: () -> Boolean): Result { + fun runWithTimeout(sourceName: String, showDialog: Boolean = false, callback: () -> Boolean): Result { val resultSignal = CompletableDeferred() val cancelSignal = Job().apply { invokeOnCompletion { @@ -107,6 +120,9 @@ class InputSourceInitializer : KoinComponent { } } + val sessionId = sessionIdCounter.getAndIncrement() + val session = InitSession(sessionId, null, sourceName, cancelSignal, resultSignal, showDialog) + scope.launch { try { val ran = callback() @@ -132,20 +148,21 @@ class InputSourceInitializer : KoinComponent { } } - val dialog = manager?.showLoadingDialogIfNeeded(sourceName, cancelSignal) + // always notify, listeners decide if they want to show UI + onInitBegin.run(session) val result = runBlocking { try { withTimeout(TIMEOUT) { resultSignal.await() } - } catch (e: TimeoutCancellationException) { + } catch (_: TimeoutCancellationException) { Result.TIMED_OUT } finally { cancelSignal.cancel() - dialog?.dispose() } } return result } + } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt index 02d33cc8..ea2d0068 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt @@ -24,17 +24,13 @@ package com.github.serivesmejia.eocvsim.input import com.github.serivesmejia.eocvsim.config.ConfigManager -import com.github.serivesmejia.eocvsim.gui.DialogFactory -import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.input.source.ImageSource import com.github.serivesmejia.eocvsim.input.source.NullSource import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler -import com.github.serivesmejia.eocvsim.util.event.MagicPhaseOrchestrable -import com.github.serivesmejia.eocvsim.util.event.PhaseOrchestrable +import com.github.serivesmejia.eocvsim.util.orchestration.PhaseOrchestrableBase import io.github.deltacv.common.util.loggerForThis -import kotlinx.coroutines.Job import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.koin.core.qualifier.named @@ -44,15 +40,11 @@ import org.opencv.core.Size import org.opencv.imgproc.Imgproc import org.openftc.easyopencv.MatRecycler import java.io.IOException -import java.util.concurrent.CancellationException -import javax.swing.JDialog -class InputSourceManager : MagicPhaseOrchestrable(), KoinComponent { +class InputSourceManager : PhaseOrchestrableBase(), KoinComponent { private val pipelineManager: PipelineManager by inject() private val configManager: ConfigManager by inject() - private val visualizer: Visualizer by inject() - private val dialogFactory: DialogFactory by inject() private val onMainLoop: EventHandler by inject(named("onMainLoop")) @@ -232,7 +224,7 @@ class InputSourceManager : MagicPhaseOrchestrable(), KoinComponent { src?.reset() if (src != null) { - when (val initResult = inputSourceInitializer.initializeWithTimeout(src, this)) { + when (val initResult = inputSourceInitializer.initializeWithTimeout(src)) { InputSourceInitializer.Result.SUCCESS -> Unit InputSourceInitializer.Result.CANCELED -> { logger.info("Input source loading canceled by user ($sourceName)") @@ -260,6 +252,7 @@ class InputSourceManager : MagicPhaseOrchestrable(), KoinComponent { return true } + @Suppress("unused") fun cleanSourceIfDirty() { currentInputSource?.cleanIfDirty() } @@ -303,20 +296,8 @@ class InputSourceManager : MagicPhaseOrchestrable(), KoinComponent { onMainLoop.once { setInputSource(name) } } - fun showLoadingDialogIfNeeded(sourceName: String?, job: Job?): JDialog? { - val type = getSourceType(sourceName) - if (type == SourceType.CAMERA || type == SourceType.VIDEO || type == SourceType.HTTP) { - return dialogFactory.createInformation( - visualizer.frame, - "Opening source...", null, "Information", "Cancel" - ) { - job?.cancel(CancellationException()) - } - } - - return null - } + @Suppress("unused") fun getDefaultInputSource() = defaultSource fun getSourceType(sourceName: String?): SourceType { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt index e5a6ee36..80ff7e53 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt @@ -27,7 +27,6 @@ import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.gui.util.WebcamDriver import com.github.serivesmejia.eocvsim.input.InputSource import com.github.serivesmejia.eocvsim.input.InputSourceInitializer -import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.github.serivesmejia.eocvsim.util.StrUtil import com.google.gson.annotations.Expose @@ -47,22 +46,20 @@ import javax.swing.filechooser.FileFilter class CameraSource : InputSource { - companion object { // for global use, -1 means no webcam currently in use @JvmStatic var currentWebcamIndex = -1 } + override val hasSlowInitialization: Boolean get() = true + @delegate:Transient private val configManager: ConfigManager by inject() - @delegate:Transient - private val inputSourceManager: InputSourceManager by inject() @Transient var webcamIndex: Int = 0 @Expose @JvmField var webcamName: String = "" - @Transient private var camera: Webcam? = null @Transient private var lastFramePaused: MatRecycler.RecyclableMat? = null @@ -260,7 +257,7 @@ class CameraSource : InputSource { } override fun onResume() { - InputSourceInitializer.runWithTimeout(name, inputSourceManager) { + InputSourceInitializer.runWithTimeout(this) { camera?.open() camera?.isOpen == true diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt index f2b1bb8b..b65dda4b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt @@ -25,12 +25,9 @@ package com.github.serivesmejia.eocvsim.input.source import com.github.serivesmejia.eocvsim.input.InputSource import com.github.serivesmejia.eocvsim.input.InputSourceInitializer -import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.google.gson.annotations.Expose import io.github.deltacv.visionloop.io.MjpegHttpReader -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject import org.opencv.core.Mat import org.opencv.core.MatOfByte import org.opencv.imgcodecs.Imgcodecs @@ -42,8 +39,7 @@ class HttpSource @JvmOverloads constructor( @Expose @JvmField var url: String = "" ) : InputSource() { - @delegate:Transient - private val inputSourceManager: InputSourceManager by inject() + override val hasSlowInitialization: Boolean get() = true @Transient private var mjpegHttpReader: MjpegHttpReader? = null @@ -134,7 +130,7 @@ class HttpSource @JvmOverloads constructor( } override fun onResume() { - InputSourceInitializer.runWithTimeout(name, inputSourceManager) { + InputSourceInitializer.runWithTimeout(this) { init() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt index bdf2f27f..e847bfd4 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt @@ -42,6 +42,8 @@ class VideoSource @JvmOverloads constructor( ) : InputSource() { + override val hasSlowInitialization: Boolean get() = true + @Transient private var video: VideoCapture? = null @Transient private val fpsLimiter = FpsLimiter(30.0) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt index 87a5c126..2578797e 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt @@ -41,7 +41,9 @@ import com.github.serivesmejia.eocvsim.util.ReflectUtil import com.github.serivesmejia.eocvsim.util.StrUtil import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler -import com.github.serivesmejia.eocvsim.util.event.MagicPhaseOrchestrable +import com.github.serivesmejia.eocvsim.util.orchestration.initDependency +import com.github.serivesmejia.eocvsim.util.orchestration.runDependency +import com.github.serivesmejia.eocvsim.util.orchestration.PhaseOrchestrableBase import com.github.serivesmejia.eocvsim.util.fps.FpsCounter import io.github.deltacv.common.image.MatPoster import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator @@ -63,22 +65,22 @@ import kotlin.coroutines.EmptyCoroutineContext import kotlin.math.roundToLong @OptIn(DelicateCoroutinesApi::class) -class PipelineManager : MagicPhaseOrchestrable(), KoinComponent { +class PipelineManager : PhaseOrchestrableBase(), KoinComponent { private val params: EOCVSim.Parameters by inject() - private val classpathScan: InitClasspathScan by initDependency(inject()) + private val classpathScan: InitClasspathScan by initDependency(inject()) private val dialogFactory: DialogFactory by inject() private val configManager: ConfigManager by inject() - private val inputSourceManager: InputSourceManager by runDependency(inject()) + private val inputSourceManager: InputSourceManager by runDependency(inject()) val pipelineStatisticsCalculator: PipelineStatisticsCalculator by inject() private val visualizer: Visualizer by inject() private val onMainUpdate: EventHandler by inject(named("onMainLoop")) private val scope: CoroutineScope by inject() - val compiledPipelineManager: CompiledPipelineManager by initDependency(inject()) + val compiledPipelineManager: CompiledPipelineManager by initDependency(inject()) companion object { var staticSnapshot: PipelineSnapshot? = null @@ -90,17 +92,22 @@ class PipelineManager : MagicPhaseOrchestrable(), KoinComponent { @JvmField val onExternalSwitchingEnable = EventHandler("OnEnableExternalPipelineSwitching") + @JvmField val onExternalSwitchingDisable = EventHandler("OnDisableExternalPipelineSwitching") @JvmField val onUpdate = EventHandler("OnPipelineUpdate") + @JvmField val onPipelineChange = EventHandler("OnPipelineChange") + @JvmField val onPipelineTimeout = EventHandler("OnPipelineTimeout") + @JvmField val onPause = EventHandler("OnPipelinePause") + @JvmField val onResume = EventHandler("OnPipelineResume") @@ -119,6 +126,7 @@ class PipelineManager : MagicPhaseOrchestrable(), KoinComponent { @Volatile var currentPipeline: OpenCvPipeline? = null private set + @Volatile var currentPipelineData: PipelineData? = null private set @@ -416,36 +424,34 @@ class PipelineManager : MagicPhaseOrchestrable(), KoinComponent { pipelineStatisticsCalculator.endFrame() } - runBlocking { - val configTimeout = configManager.config.pipelineTimeout + val configTimeout = configManager.config.pipelineTimeout - //allow double timeout if we haven't initialized the pipeline - val timeout = if (hasInitCurrentPipeline) { - configTimeout.ms - } else { - (configTimeout.ms * 1.8).roundToLong() - } + //allow double timeout if we haven't initialized the pipeline + val timeout = if (hasInitCurrentPipeline) { + configTimeout.ms + } else { + (configTimeout.ms * 1.8).roundToLong() + } - try { - //ok! this is the part in which we'll wait for the pipeline with a timeout - withTimeout(timeout) { - pipelineJob.join() - } - } catch (_: TimeoutCancellationException) { - //oops, pipeline ran out of time! we'll fall back - //to default pipeline to avoid further issues. - requestForceChangePipeline(0) - //also call the event listeners in case - //someone wants to do something here - onPipelineTimeout.run() - - logger.warn("User pipeline $currentPipelineName took too long to $lastPipelineAction (more than $timeout ms), falling back to DefaultPipeline.") - } finally { - //we cancel our pipeline job so that it - //doesn't post the output mat from the - //pipeline if it ever returns. - pipelineJob.cancel() + try { + //ok! this is the part in which we'll wait for the pipeline with a timeout + withTimeout(timeout) { + pipelineJob.join() } + } catch (_: TimeoutCancellationException) { + //oops, pipeline ran out of time! we'll fall back + //to default pipeline to avoid further issues. + requestForceChangePipeline(0) + //also call the event listeners in case + //someone wants to do something here + onPipelineTimeout.run() + + logger.warn("User pipeline $currentPipelineName took too long to $lastPipelineAction (more than $timeout ms), falling back to DefaultPipeline.") + } finally { + //we cancel our pipeline job so that it + //doesn't post the output mat from the + //pipeline if it ever returns. + pipelineJob.cancel() } } @@ -701,14 +707,14 @@ class PipelineManager : MagicPhaseOrchestrable(), KoinComponent { currentPipelineIndex = index currentPipelineData = data - forceChangePipeline( - clazz = data.clazz, - applyLatestSnapshot = applyLatestSnapshot, - applyStaticSnapshot = applyStaticSnapshot - ) - - visualizer.pipelineSelectorPanel.selectedIndex = index - } + forceChangePipeline( + clazz = data.clazz, + applyLatestSnapshot = applyLatestSnapshot, + applyStaticSnapshot = applyStaticSnapshot + ) + + visualizer.pipelineSelectorPanel.selectedIndex = index + } /** * Change to the requested pipeline only if we're diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/CompiledPipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/CompiledPipelineManager.kt index a82a36b1..eb5d9453 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/CompiledPipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/CompiledPipelineManager.kt @@ -41,19 +41,18 @@ import java.io.File import org.koin.core.component.KoinComponent import org.koin.core.component.inject import com.github.serivesmejia.eocvsim.gui.Visualizer -import com.github.serivesmejia.eocvsim.util.event.MagicPhaseOrchestrable -import com.github.serivesmejia.eocvsim.util.event.Orchestrable -import com.github.serivesmejia.eocvsim.util.event.Orchestrator +import com.github.serivesmejia.eocvsim.util.orchestration.initDependency +import com.github.serivesmejia.eocvsim.util.orchestration.PhaseOrchestrableBase import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager import org.koin.core.qualifier.named -class CompiledPipelineManager : MagicPhaseOrchestrable(), KoinComponent { +class CompiledPipelineManager : PhaseOrchestrableBase(), KoinComponent { private val onMainLoop: EventHandler by inject(named("onMainLoop")) private val scope: CoroutineScope by inject() private val pipelineManager: PipelineManager by inject() - val workspaceManager: WorkspaceManager by initDependency(inject()) + val workspaceManager: WorkspaceManager by initDependency(inject()) private val visualizer: Visualizer by inject() private val dialogFactory: DialogFactory by inject() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt index 1781f20a..ec2aaa69 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt @@ -11,14 +11,12 @@ import org.koin.core.component.KoinComponent import org.koin.core.component.inject import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.gui.Visualizer -import com.github.serivesmejia.eocvsim.util.event.MagicPhaseOrchestrable -import com.github.serivesmejia.eocvsim.util.event.Orchestrable -import com.github.serivesmejia.eocvsim.util.event.Orchestrator -import org.koin.core.qualifier.named +import com.github.serivesmejia.eocvsim.util.orchestration.dependency +import com.github.serivesmejia.eocvsim.util.orchestration.PhaseOrchestrableBase -class TunerManager : MagicPhaseOrchestrable(), KoinComponent { +class TunerManager : PhaseOrchestrableBase(), KoinComponent { - private val pipelineManager: PipelineManager by dependency(inject()) + private val pipelineManager: PipelineManager by dependency(inject()) private val visualizer: Visualizer by inject() val logger by loggerForThis() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt index 6314f65d..7d0d8ab1 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt @@ -10,11 +10,10 @@ class DoubleField( reflectionField: VirtualField ) : NumericField(instance, reflectionField, AllowMode.ONLY_NUMBERS_DECIMAL, reflectionField.get() as? Double ?: 0.0) { - override fun createNumber(value: Double): Double = value class Acceptor : TunableFieldAcceptor { override fun accept(clazz: Class<*>) = clazz == Double::class.java || clazz == java.lang.Double::class.java } -} +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt index 85c78785..222bfa5a 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt @@ -23,8 +23,8 @@ package com.github.serivesmejia.eocvsim.util -import com.github.serivesmejia.eocvsim.util.event.Orchestrable -import com.github.serivesmejia.eocvsim.util.event.Orchestrator +import com.github.serivesmejia.eocvsim.util.orchestration.Orchestrable +import com.github.serivesmejia.eocvsim.util.orchestration.Orchestrator import com.qualcomm.robotcore.eventloop.opmode.Disabled import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode import com.qualcomm.robotcore.eventloop.opmode.OpMode @@ -32,10 +32,9 @@ import com.qualcomm.robotcore.util.ElapsedTime import io.github.classgraph.ClassGraph import io.github.deltacv.common.util.loggerForThis import org.firstinspires.ftc.vision.VisionProcessor -import org.koin.core.component.KoinComponent import org.openftc.easyopencv.OpenCvPipeline -class InitClasspathScan : ClasspathScan(), Orchestrable, KoinComponent { +class InitClasspathScan : ClasspathScan(), Orchestrable { override fun wire(orchestrator: Orchestrator) { orchestrator.register(this) { phase(Orchestrator.Phase.INIT) { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt index b7b921f3..193b8c10 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt @@ -65,7 +65,6 @@ class CrashReport(causedByException: Throwable, isDummy: Boolean = false) { "Oops.", "Uh... Did I do that?", "This is fine.", - "I'm not even angry. I'm being so sincere right now.", "I feel sad now :(", "I let you down. Sorry :(", "On the bright side, I bought you a teddy bear!", @@ -85,8 +84,6 @@ class CrashReport(causedByException: Throwable, isDummy: Boolean = false) { "This doesn't make any sense!", "Why is it breaking :(", "Don't do that.", - "Ouch. That hurt :(", - "This is a token for 1 free hug. Redeem at your nearest local team: [~~HUG~~]", "But it works on my machine!" ) @@ -144,7 +141,7 @@ class CrashReport(causedByException: Throwable, isDummy: Boolean = false) { sb.appendLine("==================================").appendLine() - sb.appendLine(": Full logs").appendLine() + sb.appendLine(": Full log").appendLine() val lastLogFile = EOCVSimFolder.lastLogFile if(lastLogFile != null) { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt index 5fcb31b4..f78354e4 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt @@ -29,7 +29,8 @@ import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.pipeline.compiled.CompiledPipelineManager import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler -import com.github.serivesmejia.eocvsim.util.event.MagicPhaseOrchestrable +import com.github.serivesmejia.eocvsim.util.orchestration.initDependency +import com.github.serivesmejia.eocvsim.util.orchestration.PhaseOrchestrableBase import com.github.serivesmejia.eocvsim.util.io.FileWatcher import com.github.serivesmejia.eocvsim.workspace.config.WorkspaceConfig import com.github.serivesmejia.eocvsim.workspace.config.WorkspaceConfigLoader @@ -61,11 +62,11 @@ import java.nio.file.Paths * classloader. */ @OptIn(DelicateCoroutinesApi::class) -class WorkspaceManager : MagicPhaseOrchestrable(), KoinComponent { +class WorkspaceManager : PhaseOrchestrableBase(), KoinComponent { private val pipelineManager: PipelineManager by inject() private val compiledPipelineManager: CompiledPipelineManager by inject() - private val configManager: ConfigManager by initDependency(inject()) + private val configManager: ConfigManager by initDependency(inject()) private val params: EOCVSim.Parameters by inject() private val scope: CoroutineScope by inject() 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 b8aebd8e..a90743d1 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 @@ -32,7 +32,8 @@ import com.github.serivesmejia.eocvsim.gui.dialog.PluginOutput import com.github.serivesmejia.eocvsim.gui.dialog.PluginOutput.Companion.trimSpecials import com.github.serivesmejia.eocvsim.plugin.api.impl.EOCVSimApiImpl import com.github.serivesmejia.eocvsim.util.event.EventHandler -import com.github.serivesmejia.eocvsim.util.event.MagicPhaseOrchestrable +import com.github.serivesmejia.eocvsim.util.orchestration.initDependency +import com.github.serivesmejia.eocvsim.util.orchestration.PhaseOrchestrableBase import io.github.deltacv.common.util.loggerForThis import io.github.deltacv.common.util.loggerOf import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin @@ -52,9 +53,9 @@ import kotlin.properties.Delegates /** * Manages the loading, enabling and disabling of plugins */ -class PluginManager : MagicPhaseOrchestrable(), KoinComponent { +class PluginManager : PhaseOrchestrableBase(), KoinComponent { - private val configManager: ConfigManager by initDependency(inject()) + private val configManager: ConfigManager by initDependency(inject()) private val visualizer: Visualizer by inject() private val dialogFactory: DialogFactory by inject() From efca47905a143695194b6ddcce28b3e859f79271 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Thu, 30 Apr 2026 15:49:39 -0600 Subject: [PATCH 12/40] Implement individual apis for accessing controls on visualizer components --- .../plugin/output/PluginOutputHandler.kt | 111 ++++++++++ .../github/deltacv/eocvsim/plugin/Folders.kt | 8 +- .../github/deltacv/eocvsim/plugin/api/Api.kt | 2 +- .../eocvsim/plugin/api/VisualizerApi.kt | 38 +++- .../plugin/api/VisualizerComponentsApi.kt | 160 ++++++++++++++ .../eocvsim/plugin/loader/PluginLoader.kt | 2 +- EOCV-Sim/build.gradle | 15 +- .../com/github/serivesmejia/eocvsim/Module.kt | 3 + .../serivesmejia/eocvsim/gui/DialogFactory.kt | 9 +- .../serivesmejia/eocvsim/gui/Visualizer.kt | 8 +- .../gui/component/visualizer/TopMenuBar.kt | 9 +- .../pipeline/SourceSelectorPanel.kt | 22 +- .../eocvsim/gui/dialog/PluginOutput.kt | 196 +++++------------- .../plugin/api/impl/VisualizerApiImpl.kt | 24 ++- .../api/impl/VisualizerComponentsApiImpl.kt | 79 +++++++ .../output/VisualPluginOutputHandler.kt | 120 +++++++++++ .../plugin/loader/FilePluginLoaderImpl.kt | 20 +- .../eocvsim/plugin/loader/PluginManager.kt | 99 ++++----- .../repository/PluginRepositoryManager.kt | 83 ++++---- .../security/superaccess/SuperAccessDaemon.kt | 2 +- .../src/main/resources/opensourcelibs.txt | 8 +- 21 files changed, 702 insertions(+), 316 deletions(-) create mode 100644 Common/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/PluginOutputHandler.kt create mode 100644 Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerComponentsApi.kt create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerComponentsApiImpl.kt create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/VisualPluginOutputHandler.kt diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/PluginOutputHandler.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/PluginOutputHandler.kt new file mode 100644 index 00000000..5899d1d9 --- /dev/null +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/PluginOutputHandler.kt @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2024 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.plugin.output + +import com.github.serivesmejia.eocvsim.util.event.ParamEventHandler + +/** + * Dialog control signal - structured events replacing magic string codes. + */ +sealed class PluginDialogSignal { + /** Show the plugin dialog (focus on output tab for messages) */ + object ShowOutput : PluginDialogSignal() + + /** Show the plugin dialog (focus on plugins manager tab) */ + object ShowPlugins : PluginDialogSignal() + + /** Hide the plugin dialog */ + object Hide : PluginDialogSignal() + + /** Enable the "Continue" button (user can proceed) */ + object EnableContinue : PluginDialogSignal() + + /** Disable the "Continue" button (user must wait) */ + object DisableContinue : PluginDialogSignal() +} + +/** + * Abstraction for plugin output and UI control. + * + * PluginManager is completely UI-agnostic and uses this handler to: + * - Send output messages to the UI + * - Signal dialog visibility and button state changes + * - Wait for user continuation (e.g., when user clicks "Continue" button) + * + * The actual UI dialog is owned by Visualizer, which subscribes to these + * event handlers and manages the dialog lifecycle. + * + * Design: No magic string codes, fully structured events. + */ +interface PluginOutputHandler { + + /** + * Event handler for all output messages. + * Subscribers (e.g., Visualizer) display these in the output area. + */ + val onOutput: ParamEventHandler + + /** + * Event handler for structured dialog control signals. + * Subscribers get explicit instructions for UI state (show/hide/button state). + */ + val onDialogSignal: ParamEventHandler + + /** + * Sends an output message (logged and emitted). + * + * @param message the message to send + */ + fun sendOutput(message: String) + + /** + * Convenience method to send a message with a newline. + * + * @param message the message to send + */ + fun sendOutputLine(message: String) = sendOutput(message + "\n") + + /** + * Sends a dialog control signal (show/hide/button state). + * + * @param signal the control signal + */ + fun sendDialogSignal(signal: PluginDialogSignal) + + /** + * Waits for continuation signal from the UI (e.g., when user clicks "Continue"). + * + * This is a coroutine-suspending function that awaits until the UI signals + * completion or the timeout expires. + * + * Must be called from a coroutine context. Blocks via suspension, not threads. + * + * @param timeoutMillis timeout in milliseconds (0 = wait indefinitely) + * @return true if completed by UI, false if timeout expired + * @throws CancellationException if the coroutine is cancelled + */ + suspend fun waitForContinuation(timeoutMillis: Long = 0L): Boolean +} + + 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 index 94b4ee9f..5f40c372 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/Folders.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/Folders.kt @@ -27,7 +27,7 @@ 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 EMBEDDED_PLUGIN_FOLDER = (PLUGIN_FOLDER + File.separator + "embedded").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 +val PLUGIN_FOLDER get() = (EOCVSimFolder + File.separator + "plugins").apply { mkdir() } +val PLUGIN_CACHING_FOLDER get() = (PLUGIN_FOLDER + File.separator + "caching").apply { mkdir() } +val EMBEDDED_PLUGIN_FOLDER get() = (PLUGIN_CACHING_FOLDER + File.separator + "embedded").apply { mkdir() } +val FILESYSTEMS_FOLDER get() = (PLUGIN_FOLDER + File.separator + "filesystems").apply { mkdir() } \ No newline at end of file diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/Api.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/Api.kt index 2cb43493..c80a9504 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/Api.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/Api.kt @@ -51,7 +51,7 @@ abstract class Api(val owner: EOCVSimPlugin) { /** * Simple name of the owning plugin class, for error messages */ - val ownerName: String get() = owner::class.simpleName ?: "UnknownPlugin" + val ownerName: String get() = owner::class.simpleName ?: "???" /** * Whether this API has been disabled. diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerApi.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerApi.kt index 49ffda21..b5696ba8 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerApi.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerApi.kt @@ -34,7 +34,6 @@ import javax.swing.JPanel * sidebar tabs, and dialog creation facilities. */ abstract class VisualizerApi(owner: EOCVSimPlugin) : Api(owner) { - /** * The visualizer window frame. * @@ -61,6 +60,16 @@ abstract class VisualizerApi(owner: EOCVSimPlugin) : Api(owner) { */ abstract val sidebarApi: VisualizerSidebarApi + /** + * Access to the main OpenCV viewport API. + */ + abstract val viewportApi: VisualizerViewportApi + + /** + * Create some of the components used by the + */ + abstract val visualizerComponentsFactoryApi: VisualizerComponentsFactoryApi + /** * Factory for creating dialogs tied to the visualizer. */ @@ -71,7 +80,6 @@ abstract class VisualizerApi(owner: EOCVSimPlugin) : Api(owner) { * API for accessing and modifying the visualizer's top menu bar. */ abstract class VisualizerTopMenuBarApi(owner: EOCVSimPlugin) : Api(owner) { - /** * The "File" menu. */ @@ -95,7 +103,6 @@ abstract class VisualizerTopMenuBarApi(owner: EOCVSimPlugin) : Api(owner) { * dynamically by plugins. */ abstract class VisualizerSidebarApi(owner: EOCVSimPlugin) : Api(owner) { - /** * Fired whenever the active sidebar tab changes. */ @@ -136,7 +143,6 @@ abstract class VisualizerSidebarApi(owner: EOCVSimPlugin) : Api(owner) { * is disabled. */ abstract class Tab(owner: EOCVSimPlugin) : Api(owner) { - /** * Display title of the tab. */ @@ -163,6 +169,28 @@ abstract class VisualizerSidebarApi(owner: EOCVSimPlugin) : Api(owner) { * Sidebar tabs do not manage external resources and * require no explicit cleanup by default. */ - override fun disableApi() { } + override fun disableApi() {} } +} + +/** + * API for interacting with the visualizer's main OpenCV viewport. + */ +abstract class VisualizerViewportApi(owner: EOCVSimPlugin) : Api(owner) { + /** + * Activates the viewport. This does not affect the main + * pipeline processing, it simply enables the visualization + * of the current pipeline's output. + */ + abstract fun activate() + + /** + * Deactivates the viewport. This does not affect the main + * pipeline processing, it simply disables the visualization + * of the current pipeline's output, as it stops rendering + * incoming frames. + */ + abstract fun deactivate() + + abstract fun setFpsMeterEnabled(enabled: Boolean) } \ No newline at end of file diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerComponentsApi.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerComponentsApi.kt new file mode 100644 index 00000000..14c06901 --- /dev/null +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerComponentsApi.kt @@ -0,0 +1,160 @@ +package io.github.deltacv.eocvsim.plugin.api + +import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin +import javax.swing.JPanel + +/** + * Factory API for creating complex visualizer components as raw JPanel instances. + * + * This API provides methods to create UI panels for pipeline selection, source selection, + * and telemetry display. These panels are automatically wired to relevant events and hooks + * for seamless integration with the EOCV-Sim environment. + * + * @param owner The plugin that owns this API instance. + */ +abstract class VisualizerComponentsFactoryApi(owner: EOCVSimPlugin) : Api(owner) { + /** + * Creates a new instance of the JPanel for selecting pipelines. + * + * The panel displays a scrollable list of all available pipelines managed by + * [PipelineManagerApi]. It allows users to select a pipeline and is automatically + * wired to handle relevant events, such as pipeline addition, removal, or selection. + * + * @return A new instance of [PipelineSelectorPanelApi]. + */ + abstract fun createPipelineSelectorPanel(): PipelineSelectorPanelApi + + /** + * Creates a new instance of the JPanel for selecting input sources. + * + * The panel displays a scrollable list of all available input sources managed by + * [InputSourceManagerApi]. It allows users to select an input source and is automatically + * wired to handle relevant events, such as source addition, removal, or selection. + * + * @return A new instance of [SourceSelectorPanelApi]. + */ + abstract fun createSourceSelectorPanel(): SourceSelectorPanelApi + + /** + * Creates a new instance of the JPanel for displaying telemetry data. + * + * The panel displays a scrollable list of telemetry messages, with each message + * formatted according to the specified separators. It is designed to provide + * real-time updates and clear functionality. + * + * @return A new instance of [TelemetryPanelApi]. + */ + abstract fun createTelemetryPanel(): TelemetryPanelApi +} + +/** + * Represents the panel for selecting pipelines. + * + * This panel provides a user interface for browsing and selecting pipelines. + * It supports enabling/disabling user interaction and switching between pipelines. + * The panel is automatically updated to reflect the latest state of the pipelines. + * + * @param owner The plugin that owns this API instance. + */ +abstract class PipelineSelectorPanelApi(owner: EOCVSimPlugin) : Api(owner) { + /** + * The JPanel instance representing the UI component. + */ + abstract val jPanel: JPanel + + /** + * Indicates whether user interaction with the panel is enabled. + */ + abstract var isInteractionEnabled: Boolean + + /** + * Indicates whether switching between pipelines is allowed. + */ + abstract var allowSwitching: Boolean + + /** + * Gets the name of the currently selected pipeline, or null if no pipeline is selected. + */ + abstract val selectedPipelineName: String? + + /** + * Gets the index of the currently selected pipeline, or -1 if no pipeline is selected. + */ + abstract val selectedPipelineIndex: Int + + /** + * Refreshes the panel to reflect the latest state of the pipelines. + */ + abstract fun refresh() +} + +/** + * Represents the panel for selecting input sources. + * + * This panel provides a user interface for browsing and selecting input sources. + * It supports enabling/disabling user interaction and switching between sources. + * The panel is automatically updated to reflect the latest state of the sources. + * + * @param owner The plugin that owns this API instance. + */ +abstract class SourceSelectorPanelApi(owner: EOCVSimPlugin) : Api(owner) { + /** + * The JPanel instance representing the UI component. + */ + abstract val jPanel: JPanel + + /** + * Indicates whether user interaction with the panel is enabled. + */ + abstract var isInteractionEnabled: Boolean + + /** + * Indicates whether switching between sources is allowed. + */ + abstract var allowSwitching: Boolean + + /** + * Gets the name of the currently selected source, or null if no source is selected. + */ + abstract val selectedSourceName: String? + + /** + * Gets the index of the currently selected source, or -1 if no source is selected. + */ + abstract val selectedSourceIndex: Int + + /** + * Refreshes the panel to reflect the latest state of the sources. + */ + abstract fun refresh() +} + +/** + * Represents the panel for displaying telemetry data. + * + * This panel provides a user interface for displaying telemetry messages. + * It supports real-time updates and clearing of telemetry data. The panel + * is designed to handle large volumes of data efficiently. + * + * @param owner The plugin that owns this API instance. + */ +abstract class TelemetryPanelApi(owner: EOCVSimPlugin) : Api(owner) { + /** + * The JPanel instance representing the UI component. + */ + abstract val jPanel: JPanel + + /** + * Updates the telemetry panel with the given text, using the specified separators. + * + * @param text The telemetry data to display. + * @param captionSeparator The separator between captions and their values. + * @param itemSeparator The separator between different telemetry items. + */ + abstract fun update(text: String, captionSeparator: String = " : ", itemSeparator: String = " | ") + + /** + * Clears all telemetry data from the panel. + */ + abstract fun clear() +} \ No newline at end of file diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginLoader.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginLoader.kt index 5819d93b..37078719 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginLoader.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginLoader.kt @@ -62,7 +62,7 @@ data class PluginInfo( val nameWithVersion = "$name v$version" /** Human-readable name including version and author */ - val nameWithAuthorVersion = "$name v$version by $author" + val nameWithVersionAndAuthor = "$name v$version by $author" companion object { diff --git a/EOCV-Sim/build.gradle b/EOCV-Sim/build.gradle index d45fe2e3..e9d35df5 100644 --- a/EOCV-Sim/build.gradle +++ b/EOCV-Sim/build.gradle @@ -1,6 +1,5 @@ import io.github.fvarrui.javapackager.gradle.PackageTask -import java.nio.file.Paths import java.time.LocalDateTime import java.time.format.DateTimeFormatter @@ -84,7 +83,7 @@ dependencies { api project(':Common') api project(':Vision') - implementation 'org.jetbrains.kotlin:kotlin-stdlib' + implementation 'org.jetbrains.kotlin:kotlin-stdlib:2.3.0' implementation(platform("io.insert-koin:koin-bom:$koin_version")) implementation("io.insert-koin:koin-core") @@ -95,7 +94,7 @@ dependencies { implementation "org.slf4j:slf4j-api:$slf4j_version" implementation "org.apache.logging.log4j:log4j-api:$log4j_version" - implementation "org.apache.logging.log4j:log4j-core:$log4j_version" + implementation "org.apache.logging.log4j:log4j-core:2.25.0" implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$log4j_version" implementation "org.deltacv.steve:core:1.1.3" @@ -122,7 +121,7 @@ dependencies { 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' + implementation 'org.jboss.shrinkwrap.resolver:shrinkwrap-resolver-depchain:3.3.3' implementation 'org.jboss.shrinkwrap.resolver:shrinkwrap-resolver-impl-maven-archive:3.3.2' implementation('org.deltacv.visionloop:streaming:1.2.9') { transitive = false } @@ -167,7 +166,7 @@ public final class Build { } } -compileJava.dependsOn writeBuildClassJava -if (tasks.findByName("compileKotlin")) { - compileKotlin.dependsOn writeBuildClassJava -} \ No newline at end of file +tasks.matching { it.name in ["compileJava", "compileKotlin", "sourcesJar"] } + .configureEach { + dependsOn writeBuildClassJava + } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt index 2b486190..699a868d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt @@ -8,6 +8,8 @@ import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.github.serivesmejia.eocvsim.output.RecordingManager import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.pipeline.compiled.CompiledPipelineManager +import com.github.serivesmejia.eocvsim.plugin.output.PluginOutputHandler +import com.github.serivesmejia.eocvsim.plugin.output.VisualPluginOutputHandler import com.github.serivesmejia.eocvsim.tuner.TunerManager import com.github.serivesmejia.eocvsim.util.InitClasspathScan import com.github.serivesmejia.eocvsim.util.event.EventHandler @@ -43,6 +45,7 @@ val eocvSimModule = module { single { Visualizer() }.bindOrchestrable() single { DialogFactory() } + single { VisualPluginOutputHandler() } single { RecordingManager() } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt index 81761106..bb95e5e1 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt @@ -29,6 +29,7 @@ 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.* import com.github.serivesmejia.eocvsim.input.SourceType +import com.github.serivesmejia.eocvsim.plugin.output.PluginOutputHandler import com.github.serivesmejia.eocvsim.util.event.EventHandler import io.github.deltacv.eocvsim.plugin.loader.PluginManager import kotlinx.coroutines.CoroutineScope @@ -47,6 +48,7 @@ class DialogFactory : KoinComponent { val pluginManager: PluginManager by inject() val configManager: ConfigManager by inject() val scope: CoroutineScope by inject() + val outputHandler: PluginOutputHandler by inject() fun createYesOrNo(parent: Component?, message: String, submessage: String, result: (Int) -> Unit) { val panel = JPanel() @@ -174,11 +176,8 @@ class DialogFactory : KoinComponent { } } - fun createMavenOutput(onContinue: Runnable?): AppendDelegate { - val delegate = AppendDelegate() - invokeLater { PluginOutput(delegate, pluginManager, configManager, scope, onContinue ?: Runnable { }) } - - return delegate + fun createPluginOutput() { + invokeLater { PluginOutput(outputHandler, pluginManager, configManager, scope) } } fun createSplashScreen(closeHandler: EventHandler?) { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt index 889eae02..2c547c64 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt @@ -39,6 +39,7 @@ import com.github.serivesmejia.eocvsim.gui.component.visualizer.opmode.SidebarOp import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.PipelineSelectorPanel import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SidebarPipelineTabPanel import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SourceSelectorPanel +import com.github.serivesmejia.eocvsim.gui.dialog.PluginOutput import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.github.serivesmejia.eocvsim.output.RecordingManager import com.github.serivesmejia.eocvsim.pipeline.PipelineManager @@ -131,7 +132,6 @@ class Visualizer : PhaseOrchestrableBase(), KoinComponent { private var title = "EasyOpenCV Simulator v" + Build.standardVersionString private var titleMsg = "No pipeline" - private var beforeTitle = "" private var beforeTitleMsg = "" lateinit var colorPicker: ColorPicker @@ -164,6 +164,12 @@ class Visualizer : PhaseOrchestrableBase(), KoinComponent { title += "-dev " } + // PluginOutput is only ever created once in the lifetime + // of the software. It however does not show up right away + // upon creation, as it simply attaches to the appropriate + // EventHandler-s to show up whenever signaled to do so. + dialogFactory.createPluginOutput() + frame = JFrame() viewport.init() 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 3ae2c809..d90ca0a1 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 @@ -27,10 +27,11 @@ import com.github.serivesmejia.eocvsim.LifecycleSignal import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.Visualizer 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.compiled.CompiledPipelineManager +import com.github.serivesmejia.eocvsim.plugin.output.PluginDialogSignal +import com.github.serivesmejia.eocvsim.plugin.output.PluginOutputHandler import com.github.serivesmejia.eocvsim.util.FileFilters import com.github.serivesmejia.eocvsim.util.exception.handling.CrashReport import com.github.serivesmejia.eocvsim.workspace.util.VSCodeLauncher @@ -59,6 +60,7 @@ class TopMenuBar : JMenuBar(), KoinComponent { val visualizer: Visualizer by inject() val dialogFactory: DialogFactory by inject() val pluginManager: PluginManager by inject() + val outputHandler: PluginOutputHandler by inject() val workspaceManager: WorkspaceManager by inject() val pipelineManager: PipelineManager by inject() val onMainUpdate: EventHandler by inject(named("onMainLoop")) @@ -125,8 +127,9 @@ class TopMenuBar : JMenuBar(), KoinComponent { } val filePlugins = JMenuItem("Manage Plugins") - filePlugins.addActionListener { pluginManager.appender.append(PluginOutput.SPECIAL_OPEN_MGR)} - + filePlugins.addActionListener { + outputHandler.sendDialogSignal(PluginDialogSignal.ShowPlugins) + } mFileMenu.add(filePlugins) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt index e0479084..df35df1c 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt @@ -23,28 +23,21 @@ package com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline -import com.github.serivesmejia.eocvsim.EOCVSim -import com.github.serivesmejia.eocvsim.input.InputSourceManager -import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.gui.DialogFactory -import org.koin.core.qualifier.named import com.github.serivesmejia.eocvsim.gui.util.icon.SourcesListIconRenderer +import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.github.serivesmejia.eocvsim.pipeline.PipelineManager +import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.util.extension.clipUpperZero -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.swing.Swing +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named import java.awt.FlowLayout import java.awt.GridBagConstraints import java.awt.GridBagLayout import java.awt.event.MouseAdapter import javax.swing.* -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject - class SourceSelectorPanel : JPanel(), KoinComponent { private val inputSourceManager: InputSourceManager by inject() @@ -208,12 +201,11 @@ class SourceSelectorPanel : JPanel(), KoinComponent { } - fun updateSourcesList(): Job { + fun updateSourcesList() { SwingUtilities.invokeLater { val listModel = DefaultListModel() inputSourceManager.sortedInputSources.forEach { source -> - listModel.addElement(source.name) } @@ -225,8 +217,6 @@ class SourceSelectorPanel : JPanel(), KoinComponent { sourceSelector.selectedIndex = 0 } - - return Job() // we can't break ABI here, so we return a dummy Job } fun getIndexOf(name: String): Int { 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 1c02559b..42c660cc 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 @@ -27,56 +27,29 @@ import com.github.serivesmejia.eocvsim.LifecycleSignal import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.gui.dialog.component.BottomButtonsPanel import com.github.serivesmejia.eocvsim.gui.dialog.component.OutputPanel -import io.github.deltacv.common.util.loggerForThis +import com.github.serivesmejia.eocvsim.plugin.output.PluginDialogSignal +import com.github.serivesmejia.eocvsim.plugin.output.PluginOutputHandler +import com.github.serivesmejia.eocvsim.plugin.output.VisualPluginOutputHandler import io.github.deltacv.eocvsim.plugin.loader.FilePluginLoaderImpl 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 kotlinx.coroutines.* +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.Channel -import kotlinx.coroutines.swing.Swing import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.koin.core.qualifier.named -import java.awt.Dimension -import java.awt.GridBagConstraints -import java.awt.GridBagLayout -import java.awt.Toolkit +import java.awt.* import java.awt.datatransfer.StringSelection import javax.swing.* -import java.awt.Desktop -import javax.swing.event.ChangeEvent -import javax.swing.event.ChangeListener class PluginOutput( - appendDelegate: AppendDelegate, + val outputHandler: PluginOutputHandler, val pluginManager: PluginManager, val configManager: ConfigManager, - val scope: CoroutineScope, - val onContinue: Runnable + val scope: CoroutineScope ) : Appendable, KoinComponent { - companion object { - const val SPECIAL = "[13mck]" - - const val SPECIAL_OPEN = "$SPECIAL[OPEN]" - const val SPECIAL_OPEN_MGR = "$SPECIAL[OPEN_MGR]" - const val SPECIAL_CLOSE = "$SPECIAL[CLOSE]" - const val SPECIAL_CONTINUE = "$SPECIAL[CONTINUE]" - const val SPECIAL_FREE = "$SPECIAL[FREE]" - const val SPECIAL_SILENT = "$SPECIAL[SILENT]" - - fun String.trimSpecials(): String { - return this - .replace(SPECIAL_OPEN, "") - .replace(SPECIAL_OPEN_MGR, "") - .replace(SPECIAL_CLOSE, "") - .replace(SPECIAL_CONTINUE, "") - .replace(SPECIAL_SILENT, "") - .replace(SPECIAL_FREE, "") - } - } - private val lifecycleChannel: Channel by inject(named("lifecycle")) private val output = JDialog() @@ -90,10 +63,8 @@ class PluginOutput( private val mavenOutputPanel = OutputPanel(mavenBottomButtonsPanel) - private val logger by loggerForThis() - init { - output.isModal = true + output.isModal = false output.isAlwaysOnTop = true output.title = "Plugin Manager" @@ -109,15 +80,48 @@ class PluginOutput( output.pack() output.setSize(500, 365) - appendDelegate.subscribe(this) + // Subscribe to output messages + outputHandler.onOutput.attachPayload { message -> + SwingUtilities.invokeLater { + mavenOutputPanel.outputArea.text += message + mavenOutputPanel.outputArea.revalidate() + mavenOutputPanel.outputArea.repaint() + } + } + + // Subscribe to dialog signals + outputHandler.onDialogSignal.attachPayload { signal -> + SwingUtilities.invokeLater { + when (signal) { + PluginDialogSignal.ShowOutput -> { + output.isVisible = true + tabbedPane.selectedIndex = tabbedPane.indexOfTab("Output") + } + PluginDialogSignal.ShowPlugins -> { + output.isVisible = true + tabbedPane.selectedIndex = tabbedPane.indexOfTab("Plugins") + tabbedPane.setComponentAt(0, makePluginManagerPanel()) + } + PluginDialogSignal.Hide -> { + output.isVisible = false + } + PluginDialogSignal.EnableContinue -> { + mavenBottomButtonsPanel.continueButton.isEnabled = true + mavenBottomButtonsPanel.closeButton.isEnabled = false + } + PluginDialogSignal.DisableContinue -> { + mavenBottomButtonsPanel.continueButton.isEnabled = false + mavenBottomButtonsPanel.closeButton.isEnabled = true + } + } + } + } output.setLocationRelativeTo(null) output.defaultCloseOperation = WindowConstants.HIDE_ON_CLOSE } - @OptIn(DelicateCoroutinesApi::class) - private fun registerListeners() = scope.launch(Dispatchers.Swing) { - + private fun registerListeners() { output.addWindowListener(object : java.awt.event.WindowAdapter() { override fun windowClosing(e: java.awt.event.WindowEvent?) { if(mavenBottomButtonsPanel.continueButton.isEnabled) { @@ -130,7 +134,10 @@ class PluginOutput( mavenBottomButtonsPanel.continueButton.addActionListener { close() - onContinue.run() + // Signal the outputHandler that user clicked Continue + if (outputHandler is VisualPluginOutputHandler) { + outputHandler.signalContinuation() + } mavenBottomButtonsPanel.continueButton.isEnabled = false mavenBottomButtonsPanel.closeButton.isEnabled = true } @@ -189,7 +196,7 @@ class PluginOutput( pluginPanel.layout = GridBagLayout() val pluginNameLabel = JLabel( - "

${loader.pluginInfo.nameWithAuthorVersion}

", + "

${loader.pluginInfo.nameWithVersionAndAuthor}

", SwingConstants.CENTER ) @@ -215,7 +222,7 @@ class PluginOutput( PluginSource.EMBEDDED -> "as an embedded plugin" } - val sourceEnabled = if(loader.shouldEnable) "It was LOADED $source." else "It is DISABLED, it comes $source." + val sourceEnabled = if(loader.shouldEnable) "It was loaded $source." else "It is disabled, it comes $source." val superAccess = if(loader.hasSuperAccess) "It has super access." @@ -225,7 +232,7 @@ class PluginOutput( is FilePluginLoaderImpl -> { if(loader.pluginToml.getBoolean("super-access", false)) "It requests super access in its manifest." - else "It does not request super access in its manifest." + else "" } else -> "" } @@ -416,72 +423,18 @@ class PluginOutput( output.isVisible = false } - private fun handleSpecials(text: String): Boolean { - when(text) { - SPECIAL_FREE -> { - mavenBottomButtonsPanel.continueButton.isEnabled = false - mavenBottomButtonsPanel.closeButton.isEnabled = true - } - SPECIAL_CLOSE -> close() - SPECIAL_CONTINUE -> { - mavenBottomButtonsPanel.continueButton.isEnabled = true - mavenBottomButtonsPanel.closeButton.isEnabled = false - } - } - - if(!text.startsWith(SPECIAL_SILENT) && text != SPECIAL_CLOSE && text != SPECIAL_FREE) { - SwingUtilities.invokeLater { - SwingUtilities.invokeLater { - if(text == SPECIAL_OPEN_MGR) { - tabbedPane.selectedIndex = tabbedPane.indexOfTab("Plugins") // focus on plugins tab - } else { - tabbedPane.selectedIndex = tabbedPane.indexOfTab("Output") // focus on output tab - } - } - - tabbedPane.setComponentAt(0, makePluginManagerPanel()) - logger.info("Displaying plugin manager dialog") - - output.isVisible = true - } - } - - return text == SPECIAL_OPEN || text == SPECIAL_CLOSE || text == SPECIAL_CONTINUE || text == SPECIAL_FREE - } - override fun append(csq: CharSequence?): java.lang.Appendable { - val text = csq.toString() - - SwingUtilities.invokeLater { - if(handleSpecials(text)) return@invokeLater - - mavenOutputPanel.outputArea.text += text.trimSpecials() - mavenOutputPanel.outputArea.revalidate() - mavenOutputPanel.outputArea.repaint() - } + // ...existing code... return this } override fun append(csq: CharSequence?, start: Int, end: Int): java.lang.Appendable { - val text = csq.toString().substring(start, end) - - SwingUtilities.invokeLater { - if(handleSpecials(text)) return@invokeLater - - mavenOutputPanel.outputArea.text += text.trimSpecials() - mavenOutputPanel.outputArea.revalidate() - mavenOutputPanel.outputArea.repaint() - } + // ...existing code... return this } override fun append(c: Char): java.lang.Appendable { - SwingUtilities.invokeLater { - mavenOutputPanel.outputArea.text += c - mavenOutputPanel.outputArea.revalidate() - mavenOutputPanel.outputArea.repaint() - } - + // ...existing code... return this } @@ -524,43 +477,4 @@ class PluginOutput( add(Box.createRigidArea(Dimension(4, 0))) } } -} - - -class AppendDelegate { - private val appendables = mutableListOf() - - @Synchronized - fun subscribe(appendable: Appendable) { - appendables.add(appendable) - } - - fun subscribe(appendable: (String) -> Unit) { - appendables.add(object : Appendable { - override fun append(csq: CharSequence?): java.lang.Appendable { - appendable(csq.toString()) - return this - } - - override fun append(csq: CharSequence?, start: Int, end: Int): java.lang.Appendable { - appendable(csq.toString().substring(start, end)) - return this - } - - override fun append(c: Char): java.lang.Appendable { - appendable(c.toString()) - return this - } - }) - } - - @Synchronized - fun append(text: String) { - appendables.forEach { it.append(text) } - } - - @Synchronized - fun appendln(text: String) { - appendables.forEach { it.appendLine(text) } - } } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerApiImpl.kt index b9156e42..749b3021 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerApiImpl.kt @@ -27,10 +27,8 @@ import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.gui.component.visualizer.SidebarPanel import com.github.serivesmejia.eocvsim.gui.component.visualizer.TopMenuBar import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.DialogFactoryApi -import io.github.deltacv.eocvsim.plugin.api.VisualizerApi -import io.github.deltacv.eocvsim.plugin.api.VisualizerSidebarApi -import io.github.deltacv.eocvsim.plugin.api.VisualizerTopMenuBarApi +import io.github.deltacv.eocvsim.plugin.api.* +import io.github.deltacv.vision.external.gui.SwingOpenCvViewport class VisualizerApiImpl(owner: EOCVSimPlugin, val internalVisualizer: Visualizer) : VisualizerApi(owner) { override val frame by liveNullableApiField { internalVisualizer.frame } @@ -39,6 +37,8 @@ class VisualizerApiImpl(owner: EOCVSimPlugin, val internalVisualizer: Visualizer override val topMenuBarApi: VisualizerTopMenuBarApi by apiField { VisualizerTopMenuBarApiImpl(owner, internalVisualizer.menuBar) } override val sidebarApi: VisualizerSidebarApi by apiField { VisualizerSidebarApiImpl(owner, internalVisualizer.sidebarPanel) } + override val viewportApi: VisualizerViewportApi by apiField { VisualizerViewportApiImpl(owner, internalVisualizer.viewport) } + override val visualizerComponentsFactoryApi: VisualizerComponentsFactoryApi by apiField { VisualizerComponentsFactoryApiImpl(owner) } override val dialogFactoryApi: DialogFactoryApi by apiField { DialogFactoryApiImpl(owner, internalVisualizer) } override fun disableApi() { } @@ -94,4 +94,20 @@ private class VisualizerSidebarApiImpl(owner: EOCVSimPlugin, val internalSidebar tabs.keys.forEach { removeTab(it) } tabs.clear() } +} + +class VisualizerViewportApiImpl(owner: EOCVSimPlugin, val internalViewport: SwingOpenCvViewport) : VisualizerViewportApi(owner) { + override fun activate() = apiImpl { + internalViewport.activate() + } + + override fun deactivate() = apiImpl { + internalViewport.deactivate() + } + + override fun setFpsMeterEnabled(enabled: Boolean) = apiImpl { + internalViewport.renderer.setFpsMeterEnabled(enabled) + } + + override fun disableApi() { } } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerComponentsApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerComponentsApiImpl.kt new file mode 100644 index 00000000..3e368a53 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerComponentsApiImpl.kt @@ -0,0 +1,79 @@ +package com.github.serivesmejia.eocvsim.plugin.api.impl + +import com.github.serivesmejia.eocvsim.gui.component.visualizer.TelemetryPanel +import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.PipelineSelectorPanel +import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SourceSelectorPanel +import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin +import io.github.deltacv.eocvsim.plugin.api.PipelineSelectorPanelApi +import io.github.deltacv.eocvsim.plugin.api.SourceSelectorPanelApi +import io.github.deltacv.eocvsim.plugin.api.TelemetryPanelApi +import io.github.deltacv.eocvsim.plugin.api.VisualizerComponentsFactoryApi + +class VisualizerComponentsFactoryApiImpl(owner: EOCVSimPlugin) : VisualizerComponentsFactoryApi(owner) { + override fun createPipelineSelectorPanel() = apiImpl { PipelineSelectorPanelApiImpl(owner, PipelineSelectorPanel()) } + override fun createSourceSelectorPanel() = apiImpl { SourceSelectorPanelApiImpl(owner, SourceSelectorPanel()) } + override fun createTelemetryPanel() = apiImpl { TelemetryPanelApiImpl(owner, TelemetryPanel()) } + + override fun disableApi() { } +} + +class PipelineSelectorPanelApiImpl(owner: EOCVSimPlugin, val internalPanel: PipelineSelectorPanel) : PipelineSelectorPanelApi(owner) { + + override val jPanel by apiField(internalPanel) + + override var isInteractionEnabled: Boolean + get() = apiImpl { internalPanel.pipelineSelectorScroll.isEnabled } + set(value) = apiImpl { internalPanel.pipelineSelectorScroll.isEnabled = value } + + override var allowSwitching: Boolean + get() = apiImpl { internalPanel.allowPipelineSwitching } + set(value) = apiImpl { internalPanel.allowPipelineSwitching = value } + + override val selectedPipelineName: String? by liveApiField { internalPanel.pipelineSelector.selectedValue } + override val selectedPipelineIndex by liveApiField { internalPanel.pipelineSelector.selectedIndex } + + override fun refresh() = apiImpl { + internalPanel.updatePipelinesList() + } + + override fun disableApi() { } + +} + +class SourceSelectorPanelApiImpl(owner: EOCVSimPlugin, val internalPanel: SourceSelectorPanel) : SourceSelectorPanelApi(owner) { + + override val jPanel by apiField(internalPanel) + + override var isInteractionEnabled: Boolean + get() = apiImpl { internalPanel.sourceSelectorScroll.isEnabled } + set(value) = apiImpl { internalPanel.sourceSelectorScroll.isEnabled = value } + + override var allowSwitching: Boolean + get() = apiImpl { internalPanel.allowSourceSwitching } + set(value) = apiImpl { internalPanel.allowSourceSwitching = value } + + override val selectedSourceName: String? by liveApiField { internalPanel.sourceSelector.selectedValue } + override val selectedSourceIndex by liveApiField { internalPanel.sourceSelector.selectedIndex } + + override fun refresh() = apiImpl { + internalPanel.updateSourcesList() + } + + override fun disableApi() { } + +} + +class TelemetryPanelApiImpl(owner: EOCVSimPlugin, val internalPanel: TelemetryPanel) : TelemetryPanelApi(owner) { + override val jPanel by apiField(internalPanel) + + override fun update(text: String, captionSeparator: String, itemSeparator: String) = apiImpl { + internalPanel.updateTelemetry(text, captionSeparator, itemSeparator) + } + + override fun clear() = apiImpl { + internalPanel.telemetryList.removeAll() + } + + override fun disableApi() { } + +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/VisualPluginOutputHandler.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/VisualPluginOutputHandler.kt new file mode 100644 index 00000000..4467a93e --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/VisualPluginOutputHandler.kt @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2024 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.plugin.output + +import com.github.serivesmejia.eocvsim.util.event.ParamEventHandler +import io.github.deltacv.common.util.loggerOf +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.withTimeoutOrNull + +/** + * Concrete implementation of PluginOutputHandler. + * + * Proper coroutine-based design: + * - Uses [withTimeoutOrNull] for timeout handling (not thread hacks) + * - Emits structured events, not magic strings + * - Manages continuation via [CompletableDeferred] suspension + */ +class VisualPluginOutputHandler : PluginOutputHandler { + + private val logger by loggerOf("PluginOutputHandler") + + // Continuation signal: plugin code awaits this, UI completes it + private var continuationDeferred: CompletableDeferred = CompletableDeferred() + + override val onOutput = ParamEventHandler("PluginOutput") + override val onDialogSignal = ParamEventHandler("PluginDialogSignal") + + /** + * Sends an output message. Emitted via [onOutput] event and logged. + */ + override fun sendOutput(message: String) { + // Log (trim leading newlines/spaces) + val trimmed = message.trim() + if (trimmed.isNotEmpty()) { + logger.info(trimmed) + } + + // Emit event + onOutput.run(message) + } + + /** + * Sends a dialog control signal (structured, not magic codes). + */ + override fun sendDialogSignal(signal: PluginDialogSignal) { + onDialogSignal.run(signal) + } + + /** + * Waits for continuation from the UI. + * + * Uses [withTimeoutOrNull] for coroutine-native timeout handling: + * - No daemon threads + * - Proper suspension (not sleep) + * - Clean timeout semantics + * + * @param timeoutMillis timeout in milliseconds (0 = wait indefinitely) + * @return true if completed by UI, false if timeout expired + */ + override suspend fun waitForContinuation(timeoutMillis: Long): Boolean { + val deferred = continuationDeferred + + return if (timeoutMillis > 0L) { + // Use withTimeoutOrNull for coroutine-native timeout + val result = withTimeoutOrNull(timeoutMillis) { + deferred.await() + true + } + + if (result == null) { + // Timeout expired + logger.warn("Plugin output continuation timed out after ${timeoutMillis}ms") + false + } else { + // Completed by UI + true + } + } else { + // No timeout, wait indefinitely + deferred.await() + true + }.also { + // Reset for next cycle + continuationDeferred = CompletableDeferred() + } + } + + /** + * UI calls this to signal that continuation is complete (e.g., user clicked "Continue"). + * This allows plugin code waiting in [waitForContinuation] to resume. + */ + fun signalContinuation() { + if (!continuationDeferred.isCompleted) { + continuationDeferred.complete(Unit) + } + } +} + + diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt index 56ebad12..6dc21337 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt @@ -25,8 +25,7 @@ package io.github.deltacv.eocvsim.plugin.loader import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.config.ConfigLoader -import com.github.serivesmejia.eocvsim.gui.dialog.AppendDelegate -import com.github.serivesmejia.eocvsim.gui.dialog.PluginOutput +import com.github.serivesmejia.eocvsim.plugin.output.PluginOutputHandler import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.util.extension.hashString @@ -50,14 +49,14 @@ import java.nio.file.Path * @param classpath the classpath of the plugin * @param pluginSource the source of the plugin (file or repository) * @param pluginManager the plugin manager - * @param appender the appender to use for logging + * @param outputHandler the output handler for plugin logging */ open class FilePluginLoaderImpl( override val pluginFile: File, override val classpath: List, override val pluginSource: PluginSource, val pluginManager: PluginManager, - val appender: AppendDelegate + val outputHandler: PluginOutputHandler ) : FilePluginLoader() { val logger by loggerForThis() @@ -111,6 +110,7 @@ open class FilePluginLoaderImpl( /** * Fetch the plugin info from the plugin.toml file + * * Fills the pluginName, pluginVersion, pluginAuthor and pluginAuthorEmail fields */ fun fetchInfoFromToml() { @@ -134,11 +134,11 @@ open class FilePluginLoaderImpl( fetchInfoFromToml() if(!shouldEnable) { - appender.appendln("${PluginOutput.SPECIAL_SILENT}Plugin ${pluginInfo.name} v${pluginInfo.version} is disabled") + outputHandler.sendOutputLine("Plugin ${pluginInfo.name} v${pluginInfo.version} is disabled") return } - appender.appendln("${PluginOutput.SPECIAL_SILENT}Loading plugin ${pluginInfo.name} v${pluginInfo.version} by ${pluginInfo.version} from ${pluginSource.name}") + outputHandler.sendOutputLine("Loading plugin ${pluginInfo.name} v${pluginInfo.version} by ${pluginInfo.version} from ${pluginSource.name}") signature @@ -214,7 +214,7 @@ open class FilePluginLoaderImpl( if(!shouldEnable) return - appender.appendln("${PluginOutput.SPECIAL_SILENT}Enabling plugin ${pluginInfo.name} v${pluginInfo.version}") + outputHandler.sendOutputLine("Enabling plugin ${pluginInfo.name} v${pluginInfo.version}") plugin.onEnable() @@ -227,7 +227,7 @@ open class FilePluginLoaderImpl( override fun disable() { if(!enabled || !loaded) return - appender.appendln("${PluginOutput.SPECIAL_SILENT}Disabling plugin ${pluginInfo.name} v${pluginInfo.version}") + outputHandler.sendOutputLine("Disabling plugin ${pluginInfo.name} v${pluginInfo.version}") plugin.onDisable() @@ -262,7 +262,7 @@ class EmbeddedFilePluginLoader( resourcePath: String, classpath: List, pluginManager: PluginManager, - appender: AppendDelegate + outputHandler: PluginOutputHandler ) : FilePluginLoaderImpl( pluginFile = resourcePath.let { // extract to EMBEDDED_PLUGIN_FOLDER @@ -286,7 +286,7 @@ class EmbeddedFilePluginLoader( }, pluginSource = PluginSource.FILE, pluginManager = pluginManager, - appender = appender + outputHandler = outputHandler ) { override val hasSuperAccess = true // Embedded plugins always have super access 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 a90743d1..738d6167 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 @@ -26,28 +26,25 @@ package io.github.deltacv.eocvsim.plugin.loader import com.github.serivesmejia.eocvsim.Build import com.github.serivesmejia.eocvsim.LifecycleSignal import com.github.serivesmejia.eocvsim.config.ConfigManager -import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.Visualizer -import com.github.serivesmejia.eocvsim.gui.dialog.PluginOutput -import com.github.serivesmejia.eocvsim.gui.dialog.PluginOutput.Companion.trimSpecials import com.github.serivesmejia.eocvsim.plugin.api.impl.EOCVSimApiImpl +import com.github.serivesmejia.eocvsim.plugin.output.PluginDialogSignal +import com.github.serivesmejia.eocvsim.plugin.output.PluginOutputHandler import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.util.orchestration.initDependency import com.github.serivesmejia.eocvsim.util.orchestration.PhaseOrchestrableBase import io.github.deltacv.common.util.loggerForThis -import io.github.deltacv.common.util.loggerOf import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin import io.github.deltacv.eocvsim.plugin.repository.PluginRepositoryManager import io.github.deltacv.eocvsim.plugin.security.superaccess.SuperAccessDaemon import io.github.deltacv.eocvsim.plugin.security.superaccess.SuperAccessDaemonClient import io.github.deltacv.eocvsim.plugin.security.toMutable import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.runBlocking import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.koin.core.qualifier.named import java.io.File -import java.util.concurrent.locks.ReentrantLock -import kotlin.concurrent.withLock import kotlin.properties.Delegates /** @@ -55,9 +52,9 @@ import kotlin.properties.Delegates */ class PluginManager : PhaseOrchestrableBase(), KoinComponent { - private val configManager: ConfigManager by initDependency(inject()) + private val configManager: ConfigManager by initDependency(inject()) private val visualizer: Visualizer by inject() - private val dialogFactory: DialogFactory by inject() + private val outputHandler: PluginOutputHandler by inject() private val onMainUpdate: EventHandler by inject(named("onMainLoop")) private val lifecycleChannel: Channel by inject(named("lifecycle")) @@ -83,33 +80,8 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { private val loadedPluginHashes = mutableListOf() - private val haltLock = ReentrantLock() - private val haltCondition = haltLock.newCondition() - - val appender by lazy { - val appender = dialogFactory.createMavenOutput { - haltLock.withLock { - haltCondition.signalAll() - } - } - - val logger by loggerOf("PluginOutput") - - appender.subscribe { - if (!it.isBlank()) { - val message = it.trimSpecials() - - if (message.isNotBlank()) { - logger.info(message) - } - } - } - - appender - } - val repositoryManager by lazy { - PluginRepositoryManager(appender, onMainUpdate, { lifecycleChannel.trySend(LifecycleSignal.Restart) }, haltLock, haltCondition) + PluginRepositoryManager(outputHandler, onMainUpdate) { lifecycleChannel.trySend(LifecycleSignal.Restart) } } private val _pluginFiles = mutableListOf() @@ -139,10 +111,10 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { */ override suspend fun init() { visualizer.onInitFinished { - appender.append(PluginOutput.SPECIAL_FREE) + outputHandler.sendDialogSignal(PluginDialogSignal.Hide) } - appender.appendln(PluginOutput.SPECIAL_SILENT + "Initializing PluginManager") + outputHandler.sendOutputLine("Initializing PluginManager") superAccessDaemonClient.init() @@ -194,7 +166,7 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { } if (pluginFiles.isEmpty()) { - appender.appendln(PluginOutput.SPECIAL_SILENT + "No plugin files to load") + outputHandler.sendOutputLine("No plugin files to load") } for (pluginFile in pluginFiles) { @@ -205,13 +177,13 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { if (pluginFile in repositoryManager.resolvedFiles) PluginSource.REPOSITORY else PluginSource.FILE, this, - appender + outputHandler ) _loaders.add(loader) loader.fetchInfoFromToml() } catch (e: Throwable) { - appender.appendln("Failure creating PluginLoader for ${pluginFile.name}: ${e.message}") + outputHandler.sendOutputLine("Failure creating PluginLoader for ${pluginFile.name}: ${e.message}") logger.error("Failure creating PluginLoader for ${pluginFile.name}", e) } } @@ -226,7 +198,7 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { "/embedded_plugins/PaperVisionPlugin.jar", listOf(), this, - appender + outputHandler ) ) @@ -255,7 +227,7 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { } } } else { - appender.appendln(PluginOutput.SPECIAL_SILENT + "PaperVision plugin is already loaded, skipping embedded plugin.") + outputHandler.sendOutputLine("PaperVision plugin is already loaded, skipping embedded plugin.") } loadPlugins() @@ -273,7 +245,7 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { ) { val tempLoader = EmbeddedPluginLoader(pluginInfo, pluginClass, eocvSimApiProvider) - logger.info("Adding embedded plugin: ${pluginInfo.name} v${pluginInfo.version} by ${pluginInfo.author}") + logger.info("Adding embedded plugin: ${pluginInfo.nameWithVersionAndAuthor}") _loaders.add(tempLoader) } @@ -293,16 +265,19 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { PluginSource.EMBEDDED -> "embedded plugin" } - appender.appendln("Plugin ${loader.pluginInfo.name} by ${loader.pluginInfo.author} is already loaded. Please delete the duplicate from the $source !") + outputHandler.sendDialogSignal(PluginDialogSignal.ShowOutput) + outputHandler.sendOutputLine("Plugin ${loader.pluginInfo.nameWithVersion} is already loaded. Please delete the duplicate from the $source !") return } loader.load() loadedPluginHashes.add(hash) } catch (e: Throwable) { - appender.appendln("Failure loading ${loader.pluginInfo.name} v${loader.pluginInfo.version}:") - appender.appendln(e.message ?: "Unknown error") - logger.error("Failure loading ${loader.pluginInfo.name} v${loader.pluginInfo.version}", e) + outputHandler.sendDialogSignal(PluginDialogSignal.ShowOutput) + outputHandler.sendOutputLine("-- Failure loading ${loader.pluginInfo.nameWithVersion} --") + outputHandler.sendOutputLine("'${e.toString()}'") + + logger.error("Failure loading ${loader.pluginInfo.nameWithVersion}", e) _loaders.remove(loader) loader.kill() @@ -319,8 +294,8 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { try { loader.enable() } catch (e: Throwable) { - appender.appendln("Failure enabling ${loader.pluginInfo.name} v${loader.pluginInfo.version}: ${e.message}") - logger.error("Failure enabling ${loader.pluginInfo.name} v${loader.pluginInfo.version}", e) + outputHandler.sendOutputLine("Failure enabling ${loader.pluginInfo.nameWithVersion}: ${e.message}") + logger.error("Failure enabling ${loader.pluginInfo.nameWithVersion}", e) loader.kill() } } @@ -338,8 +313,8 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { try { loader.disable() } catch (e: Throwable) { - appender.appendln("Failure disabling ${loader.pluginInfo.name} v${loader.pluginInfo.version}: ${e.message}") - logger.error("Failure disabling ${loader.pluginInfo.name} v${loader.pluginInfo.version}", e) + outputHandler.sendOutputLine("Failure disabling ${loader.pluginInfo.nameWithVersion}: ${e.message}") + logger.error("Failure disabling ${loader.pluginInfo.nameWithVersion}", e) loader.kill() } } @@ -356,13 +331,15 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { */ fun requestSuperAccessFor(loader: PluginLoader, reason: String): Boolean { if (loader.hasSuperAccess) { - appender.appendln(PluginOutput.SPECIAL_SILENT + "Plugin ${loader.pluginInfo.name} v${loader.pluginInfo.version} already has super access") + outputHandler.sendOutputLine("Plugin ${loader.pluginInfo.name} v${loader.pluginInfo.version} already has super access") return true } val signature = loader.signature - appender.appendln(PluginOutput.SPECIAL_SILENT + "Requesting super access for ${loader.pluginInfo.name} v${loader.pluginInfo.version}") + outputHandler.sendOutputLine("Requesting super access for ${loader.pluginInfo.name} v${loader.pluginInfo.version}") + outputHandler.sendDialogSignal(PluginDialogSignal.ShowOutput) + outputHandler.sendDialogSignal(PluginDialogSignal.EnableContinue) if (loader is FilePluginLoaderImpl) { var access = false @@ -374,24 +351,20 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { reason ) ) { - if (it) { - access = true - } - - haltLock.withLock { - haltCondition.signalAll() - } + access = it } - haltLock.withLock { - haltCondition.await() + // Block until user continues (uses runBlocking since this is sync context) + runBlocking { + outputHandler.waitForContinuation(15000L) // 15 second timeout } - appender.appendln(PluginOutput.SPECIAL_SILENT + "Super access for ${loader.pluginInfo.nameWithVersion} was ${if (access) "granted" else "denied"}") + outputHandler.sendDialogSignal(PluginDialogSignal.DisableContinue) + outputHandler.sendOutputLine("Super access for ${loader.pluginInfo.nameWithVersion} was ${if (access) "granted" else "denied"}") return access } else { - appender.appendln(PluginOutput.SPECIAL_SILENT + "Super access for ${loader.pluginInfo.nameWithVersion} is automatically determined, it was ${if (loader.hasSuperAccess) "granted" else "denied"}") + outputHandler.sendOutputLine("Super access for ${loader.pluginInfo.nameWithVersion} is automatically determined, it was ${if (loader.hasSuperAccess) "granted" else "denied"}") return loader.hasSuperAccess } } diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt index e9945014..7cb82853 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt @@ -23,34 +23,26 @@ package io.github.deltacv.eocvsim.plugin.repository -import com.github.serivesmejia.eocvsim.EOCVSim -import com.github.serivesmejia.eocvsim.gui.dialog.AppendDelegate -import com.github.serivesmejia.eocvsim.gui.dialog.PluginOutput +import com.github.serivesmejia.eocvsim.plugin.output.PluginDialogSignal +import com.github.serivesmejia.eocvsim.plugin.output.PluginOutputHandler import com.github.serivesmejia.eocvsim.util.SysUtil +import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.util.extension.hashString import com.github.serivesmejia.eocvsim.util.extension.plus -import io.github.deltacv.common.util.loggerForThis import com.moandjiezana.toml.Toml import io.github.deltacv.common.util.ParsedVersion +import io.github.deltacv.common.util.loggerForThis import io.github.deltacv.eocvsim.plugin.loader.PluginManager +import kotlinx.coroutines.runBlocking import org.jboss.shrinkwrap.resolver.api.maven.ConfigurableMavenResolverSystem import org.jboss.shrinkwrap.resolver.api.maven.Maven import java.io.File -import java.util.concurrent.TimeUnit -import java.util.concurrent.locks.Condition -import java.util.concurrent.locks.ReentrantLock import javax.swing.JOptionPane -import kotlin.concurrent.withLock - - -import com.github.serivesmejia.eocvsim.util.event.EventHandler class PluginRepositoryManager( - val appender: AppendDelegate, + val outputHandler: PluginOutputHandler, val onMainUpdate: EventHandler, - val restart: () -> Unit, - val haltLock: ReentrantLock, - val haltCondition: Condition + val restart: () -> Unit ) { companion object { @@ -83,7 +75,6 @@ class PluginRepositoryManager( fun init() { logger.info("Initializing...") - appender // init appender SysUtil.copyFileIs(CACHE_TOML_RES, CACHE_FILE, false) cacheToml = Toml().read(CACHE_FILE) @@ -153,18 +144,16 @@ class PluginRepositoryManager( val latest = checkForUpdates(pluginDep, repositories.toTypedArray()) if(latest != null) { - appender.appendln( - PluginOutput.SPECIAL_SILENT + - "Plugin \"${plugin.key}\" is outdated. Latest version is ${latest.version}." + outputHandler.sendOutputLine( + "Plugin \"${plugin.key}\" is outdated. Latest version is ${latest.version}." ) onMainUpdate.once { promptUpdateAndRestart(plugin.key, pluginDep, latest) } } else { - appender.appendln( - PluginOutput.SPECIAL_SILENT + - "Plugin \"${plugin.key}\" is up to date." + outputHandler.sendOutputLine( + "Plugin \"${plugin.key}\" is up to date." ) } } @@ -178,7 +167,7 @@ class PluginRepositoryManager( private fun promptUpdateAndRestart(pluginName: String, pluginDep: String, latest: ParsedVersion) { if (promptUpdate(pluginName, latest)) { - appender.appendln(PluginOutput.SPECIAL_SILENT +"Updating plugin \"$pluginName\" to version ${latest.version}...") + outputHandler.sendOutputLine("Updating plugin \"$pluginName\" to version ${latest.version}...") val artifact = parseArtifact(pluginDep) @@ -190,7 +179,7 @@ class PluginRepositoryManager( // Locate the `[plugins]` section val indexOfPlugins = tomlLines.indexOfFirst { it.trim() == "[plugins]" } if (indexOfPlugins == -1) { - appender.appendln("Failed to find [plugins] section in the TOML file.") + outputHandler.sendOutputLine("Failed to find [plugins] section in the TOML file.") return } @@ -202,7 +191,7 @@ class PluginRepositoryManager( ?.let { it + indexOfPlugins + 1 } // Adjust the index relative to the full list if (pluginLineIndex == -1 || pluginLineIndex == null) { - appender.appendln("Failed to find plugin \"$pluginName\" in the TOML file.") + outputHandler.sendOutputLine("Failed to find plugin \"$pluginName\" in the TOML file.") return } @@ -213,7 +202,7 @@ class PluginRepositoryManager( // Write updated content back to the TOML file tomlFile.writeText(tomlLines.joinToString("\n")) - appender.appendln(PluginOutput.SPECIAL_SILENT +"Successfully updated \"$pluginName\" to version ${latest.version}. Restarting...") + outputHandler.sendOutputLine("Successfully updated \"$pluginName\" to version ${latest.version}. Restarting...") restart() } } @@ -248,15 +237,13 @@ class PluginRepositoryManager( if (cachedFile.exists() && areAllTransitivesCached(pluginDep)) { addToResolvedFiles(cachedFile, pluginDep, newCache, newTransitiveCache) - appender.appendln( - PluginOutput.SPECIAL_SILENT + - "Found cached plugin \"$pluginDep\" (${pluginDep.hashString}). All transitive dependencies OK." + outputHandler.sendOutputLine( + "Found cached plugin \"$pluginDep\" (${pluginDep.hashString}). All transitive dependencies OK." ) return true } else { - appender.appendln( - PluginOutput.SPECIAL_SILENT + - "Dependency missing for plugin $pluginDep. Resolving..." + outputHandler.sendOutputLine( + "Dependency missing for plugin $pluginDep. Resolving..." ) } } @@ -274,14 +261,12 @@ class PluginRepositoryManager( val matchesDepsHash = depsHash == tomlHash if(!matchesDepsHash) { - appender.appendln( - PluginOutput.SPECIAL_SILENT + - "Mismatch, $depsHash != $tomlHash" + outputHandler.sendOutputLine( + "Mismatch, $depsHash != $tomlHash" ) - appender.appendln( - PluginOutput.SPECIAL_SILENT + - "Transitive dependencies hash mismatch for plugin $pluginDep. Resolving..." + outputHandler.sendOutputLine( + "Transitive dependencies hash mismatch for plugin $pluginDep. Resolving..." ) } @@ -292,9 +277,8 @@ class PluginRepositoryManager( val exists = File(it).exists() if(!exists) { - appender.appendln( - PluginOutput.SPECIAL_SILENT + - "Couldn't find file specified in cache for plugin $pluginDep, expected at \"$it\"." + outputHandler.sendOutputLine( + "Couldn't find file specified in cache for plugin $pluginDep, expected at \"$it\"." ) } @@ -308,7 +292,7 @@ class PluginRepositoryManager( newCache: MutableMap, newTransitiveCache: MutableMap> ): File { - appender.appendln("Resolving plugin \"$pluginDep\"...") + outputHandler.sendOutputLine("Resolving plugin \"$pluginDep\"...") var pluginJar: File? = null @@ -350,18 +334,23 @@ class PluginRepositoryManager( // Function to handle resolution errors private fun handleResolutionError(pluginDep: String, ex: Exception) { logger.warn("Failed to resolve plugin dependency \"$pluginDep\"", ex) - appender.appendln("Failed to resolve plugin \"$pluginDep\": ${ex.message}") + outputHandler.sendOutputLine("Failed to resolve plugin \"$pluginDep\": ${ex.message}") } // Function to handle the outcome of the resolution process private fun handleResolutionOutcome(shouldHalt: Boolean) { if (shouldHalt) { - appender.append(PluginOutput.SPECIAL_CONTINUE) - haltLock.withLock { - haltCondition.await(15, TimeUnit.SECONDS) // wait for user to read the error + // Show dialog and enable Continue button if errors occurred + outputHandler.sendDialogSignal(PluginDialogSignal.ShowOutput) + outputHandler.sendDialogSignal(PluginDialogSignal.EnableContinue) + + // Block for up to 15 seconds waiting for user to read the error + runBlocking { + outputHandler.waitForContinuation(15000L) } } else { - appender.append(PluginOutput.SPECIAL_CLOSE) + // Close dialog if everything resolved successfully + outputHandler.sendDialogSignal(PluginDialogSignal.Hide) } } 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 b6612f8b..e4afb42a 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 @@ -163,7 +163,7 @@ object SuperAccessDaemon { val reason = message.reason - val name = parser.nameWithAuthorVersion + val name = parser.nameWithVersionAndAuthor var warning = "$GENERIC_SUPERACCESS_WARN" if(reason.trim().isNotBlank()) { diff --git a/EOCV-Sim/src/main/resources/opensourcelibs.txt b/EOCV-Sim/src/main/resources/opensourcelibs.txt index ce70c5a7..2adc8109 100644 --- a/EOCV-Sim/src/main/resources/opensourcelibs.txt +++ b/EOCV-Sim/src/main/resources/opensourcelibs.txt @@ -1,17 +1,13 @@ -EOCV-Sim and its source code is distributed under the MIT License +EOCV-Sim and its source code is distributed under the MIT License OpenCV - Under Apache 2.0 License OpenPnP OpenCV - Under Apache 2.0 License FTC SDK - Some source code under BSD License EasyOpenCV - Some source code under MIT License EOCV-AprilTag-Plugin - Source code under MIT License -webcam-capture - Under MIT License Skiko - Under Apache 2.0 License Gson - Under Apache 2.0 License ClassGraph - Under MIT License FlatLaf - Under Apache 2.0 License Kotlin stdlib & coroutines - Under Apache 2.0 License -picocli - Under Apache 2.0 License -dear imgui - Under MIT License -imgui-java - Under Apache 2.0 License -LWJGL - Under BSD 3-Clause License \ No newline at end of file +picocli - Under Apache 2.0 License \ No newline at end of file From 92ed92a07fd7653d49eac7a53f45ce550ac52a9f Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Fri, 8 May 2026 18:51:20 -0600 Subject: [PATCH 13/40] Fix issue where onDrawFrame was sometimes called before init & fix some PluginManger output messages & continuation handling --- .../plugin/output/PluginOutputHandler.kt | 7 ++- .../com/github/serivesmejia/eocvsim/Module.kt | 2 +- .../eocvsim/pipeline/PipelineManager.kt | 3 +- .../output/VisualPluginOutputHandler.kt | 15 ++++-- .../plugin/loader/FilePluginLoaderImpl.kt | 6 +-- .../eocvsim/plugin/loader/PluginManager.kt | 13 +++-- EOCV-Sim/src/main/resources/.lock | 49 ------------------- 7 files changed, 28 insertions(+), 67 deletions(-) diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/PluginOutputHandler.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/PluginOutputHandler.kt index 5899d1d9..46193521 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/PluginOutputHandler.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/PluginOutputHandler.kt @@ -106,6 +106,9 @@ interface PluginOutputHandler { * @throws CancellationException if the coroutine is cancelled */ suspend fun waitForContinuation(timeoutMillis: Long = 0L): Boolean -} - + /** + * Programmatically signal that continuation is complete. + */ + fun signalContinuation() +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt index 699a868d..d66a5de2 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt @@ -32,7 +32,7 @@ val eocvSimModule = module { single { InitClasspathScan() }.bindOrchestrable() // global scope for launching coroutines within the app - single { CoroutineScope(SupervisorJob() + Dispatchers.Default) } + single { CoroutineScope(SupervisorJob() + Dispatchers.Default) } single { ConfigManager() }.bindOrchestrable() single { WorkspaceManager() }.bindOrchestrable() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt index 2578797e..74d38405 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt @@ -625,6 +625,7 @@ class PipelineManager : PhaseOrchestrableBase(), KoinComponent { previousPipelineIndex = currentPipelineIndex previousPipeline = currentPipeline + hasInitCurrentPipeline = false currentPipeline = nextPipeline currentTelemetry = nextTelemetry currentPipelineName = clazz.simpleName @@ -646,8 +647,6 @@ class PipelineManager : PhaseOrchestrableBase(), KoinComponent { if (applyStaticSnapshot) staticSnapshot?.transferTo(currentPipeline!!) - hasInitCurrentPipeline = false - currentPipelineContext?.close() currentPipelineContext = newSingleThreadContext("Pipeline-$currentPipelineName") activePipelineContexts.add(currentPipelineContext!!) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/VisualPluginOutputHandler.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/VisualPluginOutputHandler.kt index 4467a93e..3cccae69 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/VisualPluginOutputHandler.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/VisualPluginOutputHandler.kt @@ -27,6 +27,7 @@ import com.github.serivesmejia.eocvsim.util.event.ParamEventHandler import io.github.deltacv.common.util.loggerOf import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.withTimeoutOrNull +import kotlin.time.Duration.Companion.milliseconds /** * Concrete implementation of PluginOutputHandler. @@ -79,11 +80,17 @@ class VisualPluginOutputHandler : PluginOutputHandler { * @return true if completed by UI, false if timeout expired */ override suspend fun waitForContinuation(timeoutMillis: Long): Boolean { + if (timeoutMillis > 0L) { + sendOutputLine("Waiting for confirmation for ${timeoutMillis / 1000} seconds...") + } else { + sendOutputLine("Waiting for confirmation...") + } + val deferred = continuationDeferred return if (timeoutMillis > 0L) { // Use withTimeoutOrNull for coroutine-native timeout - val result = withTimeoutOrNull(timeoutMillis) { + val result = withTimeoutOrNull(timeoutMillis.milliseconds) { deferred.await() true } @@ -110,11 +117,9 @@ class VisualPluginOutputHandler : PluginOutputHandler { * UI calls this to signal that continuation is complete (e.g., user clicked "Continue"). * This allows plugin code waiting in [waitForContinuation] to resume. */ - fun signalContinuation() { + override fun signalContinuation() { if (!continuationDeferred.isCompleted) { continuationDeferred.complete(Unit) } } -} - - +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt index 6dc21337..7a940e83 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt @@ -134,11 +134,11 @@ open class FilePluginLoaderImpl( fetchInfoFromToml() if(!shouldEnable) { - outputHandler.sendOutputLine("Plugin ${pluginInfo.name} v${pluginInfo.version} is disabled") + outputHandler.sendOutputLine("Plugin ${pluginInfo.nameWithVersion} is disabled") return } - outputHandler.sendOutputLine("Loading plugin ${pluginInfo.name} v${pluginInfo.version} by ${pluginInfo.version} from ${pluginSource.name}") + outputHandler.sendOutputLine("Loading plugin ${pluginInfo.nameWithVersionAndAuthor} from ${pluginSource.name}") signature @@ -214,7 +214,7 @@ open class FilePluginLoaderImpl( if(!shouldEnable) return - outputHandler.sendOutputLine("Enabling plugin ${pluginInfo.name} v${pluginInfo.version}") + outputHandler.sendOutputLine("Enabling plugin ${pluginInfo.nameWithVersionAndAuthor}") plugin.onEnable() 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 738d6167..b6608d71 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 @@ -279,6 +279,12 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { logger.error("Failure loading ${loader.pluginInfo.nameWithVersion}", e) + outputHandler.sendDialogSignal(PluginDialogSignal.EnableContinue) + runBlocking { + outputHandler.waitForContinuation(15000L) + } + outputHandler.sendDialogSignal(PluginDialogSignal.DisableContinue) + _loaders.remove(loader) loader.kill() } @@ -338,8 +344,6 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { val signature = loader.signature outputHandler.sendOutputLine("Requesting super access for ${loader.pluginInfo.name} v${loader.pluginInfo.version}") - outputHandler.sendDialogSignal(PluginDialogSignal.ShowOutput) - outputHandler.sendDialogSignal(PluginDialogSignal.EnableContinue) if (loader is FilePluginLoaderImpl) { var access = false @@ -352,14 +356,13 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { ) ) { access = it + outputHandler.signalContinuation() } - // Block until user continues (uses runBlocking since this is sync context) runBlocking { - outputHandler.waitForContinuation(15000L) // 15 second timeout + outputHandler.waitForContinuation(0L) // Wait indefinitely. } - outputHandler.sendDialogSignal(PluginDialogSignal.DisableContinue) outputHandler.sendOutputLine("Super access for ${loader.pluginInfo.nameWithVersion} was ${if (access) "granted" else "denied"}") return access diff --git a/EOCV-Sim/src/main/resources/.lock b/EOCV-Sim/src/main/resources/.lock index fedd58f7..e69de29b 100644 --- a/EOCV-Sim/src/main/resources/.lock +++ b/EOCV-Sim/src/main/resources/.lock @@ -1,49 +0,0 @@ -de las olas dulce, salina sea un amor, -colgadas las estrellas de tal perdición, -y la luz baila en sus ojos, que atraviesan, -por una noche triste, no será el dolor. - -un beso de miel, por dos de caramelo, -suena cada toque que ellos no lamentan, -y el cielo cae en sus risas, que perdonan, -por una noche vieja, la que si fue ayer. - -en la tumba del sol, seremos tan felices, -vuelan por las brisas de una nueva vida, -vuelan y aterrizan en otro frío octubre, -por una noche infinita, sea tan deprisa. - -coronado el rey, en la luna y una tierra, -que ellos nunca dejarán de tal querer, -y el río deja de fluir, que no para de amar, -por una noche sola, sin luces de ciudad. - -el no deja de quererlo, el no deja de amarlo, -un loco tan loco por unos ojos tan tristes, -un loco tan amado por su risa inconfundible, -por una noche de gracias, una nunca será. - -sus brazos se rinden, la corriente los lleva, -sus dias e historias que no serán grises, -viento que habla, tiene mucho por decir, -por una noche en silencio, sola murmulla. - -que se caiga el cielo, que brille en el limbo, -y amaría la manera como solías sostenerme, -de un toque lleno de amor, acaba en tristeza, -por una noche nuestra, bajo ninguna mirada. - -tras un extraño silencio del sol ausente, -aquel que lamenta más que mil palabras, -¿y qué estaba pasando?, ¿ahora qué será? -por una noche sombría, en ella te veo bailar. - -en tanto cariño por un solo dolor, de pronto, -una pizca de café que brilla en sus luces, -tanto que divagan sin saber sus nombres, -por una noche perdida, ¿en dónde estará? - -se quiebra el infinito, parpadeo de instante, -bailando y brillando, sola en una ausencia, -sin sentir piedad, él se va sin mirar atrás, -y la noche es infinita, que no para de llorar. \ No newline at end of file From 0d3cc0517febe136674063c8798d80356d2a9a59 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Sun, 10 May 2026 13:02:47 -0600 Subject: [PATCH 14/40] Remove test stage from webcam creation dialog --- .../gui/dialog/source/CreateCameraSource.kt | 84 +++---------------- 1 file changed, 13 insertions(+), 71 deletions(-) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt index eabc9e24..4538c7a0 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt @@ -39,7 +39,6 @@ import io.github.deltacv.steve.openpnp.OpenPnpBackend import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import org.opencv.core.Mat import org.opencv.core.Size import java.awt.* import javax.swing.* @@ -60,7 +59,6 @@ class CreateCameraSource : KoinComponent { } val createCameraSource = JDialog(visualizer.frame) - private val statusLabel = JLabel("", SwingConstants.CENTER) private val camerasComboBox = JComboBox() private val dimensionsComboBox = JComboBox() @@ -73,15 +71,14 @@ class CreateCameraSource : KoinComponent { ) { WebcamRotation.fromDisplayName(it) ?: WebcamRotation.UPRIGHT } private val nameTextField = JTextField(15) - private val createButton = JButton() - private var wasCancelled = false + private val createButton = JButton("Create") private var webcams = listOf() private val indexes = mutableMapOf() private var usingOpenCvDiscovery = false private var state = State.INITIAL - private enum class State { INITIAL, CLICKED_TEST, TEST_SUCCESSFUL, TEST_FAILED, NO_WEBCAMS, UNSUPPORTED } + private enum class State { INITIAL, NO_WEBCAMS, UNSUPPORTED } init { // Force preferred driver fallback @@ -90,7 +87,6 @@ class CreateCameraSource : KoinComponent { val preferredDriver = configManager.config.preferredWebcamDriver - when (preferredDriver) { WebcamDriver.OpenPnp -> { Webcam.backend = OpenPnpBackend @@ -181,9 +177,6 @@ class CreateCameraSource : KoinComponent { gbc.gridx = 0; gbc.gridy = 0 contentsPanel.add(fieldsPanel, gbc) - gbc.gridy = 1 - contentsPanel.add(statusLabel, gbc) - // Bottom buttons val bottomPanel = JPanel(GridBagLayout()) val gbcBtts = GridBagConstraints().apply { insets = Insets(0, 0, 0, 10) } @@ -193,7 +186,7 @@ class CreateCameraSource : KoinComponent { bottomPanel.add(cancelButton, gbcBtts) gbc.insets = Insets(10, 0, 0, 0) - gbc.gridx = 0; gbc.gridy = 2 + gbc.gridx = 0; gbc.gridy = 1 contentsPanel.add(bottomPanel, gbc) contentsPanel.border = BorderFactory.createEmptyBorder(8, 15, 15, 0) @@ -205,7 +198,7 @@ class CreateCameraSource : KoinComponent { camerasComboBox.addActionListener { onCameraSelectionChanged() } nameTextField.document.addDocumentListener(SimpleDocumentListener { updateCreateButton() }) - cancelButton.addActionListener { wasCancelled = true; close() } + cancelButton.addActionListener { close() } updateState() @@ -219,36 +212,17 @@ class CreateCameraSource : KoinComponent { } private fun onCreateButton() { - if (state == State.TEST_SUCCESSFUL) { - val webcam = webcams[getSelectedIndex()] - val dim = sizes[camerasComboBox.selectedItem]!![dimensionsComboBox.selectedIndex] - val rotation = rotationComboBox.selectedEnum ?: WebcamRotation.UPRIGHT // Correction: Handle null case - - if (usingOpenCvDiscovery) { - val index = if (webcam is OpenCvWebcam) webcam.index else camerasComboBox.selectedIndex - createSource(nameTextField.text, index, dim, rotation) - } else { - createSource(nameTextField.text, webcam.name, dim, rotation) - } - close() - } else { - state = State.CLICKED_TEST - updateState() - CoroutineScope(Dispatchers.IO).launch { - val webcam = webcams[getSelectedIndex()] - val dim = sizes[camerasComboBox.selectedItem]!![dimensionsComboBox.selectedIndex] + val webcam = webcams[getSelectedIndex()] + val dim = sizes[camerasComboBox.selectedItem]!![dimensionsComboBox.selectedIndex] + val rotation = rotationComboBox.selectedEnum ?: WebcamRotation.UPRIGHT - webcam.resolution = dim - val success = testCamera(webcam) - - SwingUtilities.invokeLater { - if (!wasCancelled) { - state = if (success) State.TEST_SUCCESSFUL else State.TEST_FAILED - updateState() - } - } - } + if (usingOpenCvDiscovery) { + val index = if (webcam is OpenCvWebcam) webcam.index else camerasComboBox.selectedIndex + createSource(nameTextField.text, index, dim, rotation) + } else { + createSource(nameTextField.text, webcam.name, dim, rotation) } + close() } private fun onCameraSelectionChanged() { @@ -274,48 +248,16 @@ class CreateCameraSource : KoinComponent { } } - private fun testCamera(webcam: Webcam): Boolean { - webcam.open() - var success = webcam.isOpen - if (success) { - val m = Mat() - try { webcam.read(m) } catch (_: Exception) { success = false } - m.release() - webcam.close() - } - return success - } - private fun updateState() { when (state) { State.INITIAL -> { - statusLabel.text = "Click \"test\" to test camera." - createButton.text = "Test" - setInteractables(true) - } - State.CLICKED_TEST -> { - statusLabel.text = "Trying to open camera, please wait..." - setInteractables(false) - } - State.TEST_SUCCESSFUL -> { - statusLabel.text = "Camera was opened successfully." - createButton.text = "Create" - setInteractables(true) - } - State.TEST_FAILED -> { - statusLabel.text = "Failed to open camera, try another one." - createButton.text = "Test" setInteractables(true) } State.NO_WEBCAMS -> { - statusLabel.text = "No cameras detected." - createButton.text = "Test" nameTextField.text = "" setInteractables(false) } State.UNSUPPORTED -> { - statusLabel.text = "This camera is currently unavailable." - createButton.text = "Test" nameTextField.text = "" setInteractables(false) camerasComboBox.isEnabled = true From e743b27de51315bcbf3985fe05565c2c2631a366 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 12 May 2026 01:09:33 -0600 Subject: [PATCH 15/40] Migrate to WPILib's opencv packaging & cscore for webcam handling --- Common/build.gradle | 28 +- EOCV-Sim/build.gradle | 58 ++-- .../github/serivesmejia/eocvsim/EOCVSim.kt | 55 +--- .../gui/dialog/source/CreateCameraSource.kt | 188 +++-------- .../eocvsim/input/source/CameraSource.kt | 179 ++++------ .../eocvsim/util/CombinedRuntimeLoader.java | 305 ++++++++++++++++++ .../eocvsim/util/LibraryLoader.java | 36 +++ .../EOCVSimUncaughtExceptionHandler.kt | 6 +- .../control/CameraSourceExposureControl.kt | 136 ++++++-- .../serivesmejia/eocvsim/test/CoreTests.kt | 3 +- Vision/build.gradle | 24 +- build.common.gradle | 4 +- build.gradle | 97 +++--- gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle | 7 +- 15 files changed, 706 insertions(+), 422 deletions(-) create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/CombinedRuntimeLoader.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/LibraryLoader.java diff --git a/Common/build.gradle b/Common/build.gradle index ebfd202f..b8d4cb53 100644 --- a/Common/build.gradle +++ b/Common/build.gradle @@ -2,6 +2,7 @@ plugins { id 'kotlin' id 'signing' id 'com.gradleup.shadow' + id 'org.photonvision.tools.WpilibTools' id "com.vanniktech.maven.publish" version "0.30.0" } @@ -9,20 +10,29 @@ 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/*" - } +wpilibTools.deps.wpilibVersion = wpilibVersion + +def nativeConfigName = 'wpilibNatives' +def nativeConfig = configurations.create(nativeConfigName) + +def nativeTasks = wpilibTools.createExtractionTasks { + configurationName = nativeConfigName } +nativeTasks.addToSourceSetResources(sourceSets.main) + +nativeConfig.dependencies.add wpilibTools.deps.wpilib("wpiutil") +nativeConfig.dependencies.add wpilibTools.deps.wpilib("cscore") +nativeConfig.dependencies.add wpilibTools.deps.wpilibOpenCv(opencvVersion) + dependencies { - api "org.openpnp:opencv:$opencv_version" + // WPILib OpenCV replaces openpnp + api wpilibTools.deps.wpilibOpenCvJava(opencvVersion) + wpilibNatives wpilibTools.deps.wpilibOpenCv(opencvVersion) implementation "com.moandjiezana.toml:toml4j:$toml4j_version" implementation "info.picocli:picocli:$picocli_version" @@ -34,12 +44,8 @@ dependencies { implementation "com.formdev:flatlaf:$flatlaf_version" implementation "com.miglayout:miglayout-swing:$miglayout_version" - // Compatibility: Skiko supports many platforms but we will only be adding - // those that are supported by AprilTagDesktop as well - implementation("org.jetbrains.skiko:skiko-awt-runtime-windows-x64:$skiko_version") implementation("org.jetbrains.skiko:skiko-awt-runtime-linux-x64:$skiko_version") - implementation("org.jetbrains.skiko:skiko-awt-runtime-linux-x64:$skiko_version") implementation("org.jetbrains.skiko:skiko-awt-runtime-linux-arm64:$skiko_version") implementation("org.jetbrains.skiko:skiko-awt-runtime-macos-x64:$skiko_version") implementation("org.jetbrains.skiko:skiko-awt-runtime-macos-arm64:$skiko_version") diff --git a/EOCV-Sim/build.gradle b/EOCV-Sim/build.gradle index e9d35df5..26469090 100644 --- a/EOCV-Sim/build.gradle +++ b/EOCV-Sim/build.gradle @@ -6,10 +6,9 @@ import java.time.format.DateTimeFormatter plugins { id 'org.jetbrains.kotlin.jvm' id 'com.gradleup.shadow' - + id 'org.photonvision.tools.WpilibTools' id 'signing' id "com.vanniktech.maven.publish" version "0.30.0" - id 'io.github.fvarrui.javapackager.plugin' } @@ -27,14 +26,6 @@ sourceSets { } } -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() } @@ -47,9 +38,7 @@ apply from: '../test-logging.gradle' tasks.register('pack', PackageTask) { dependsOn build - // mandatory mainClass = 'com.github.serivesmejia.eocvsim.Main' - // optional bundleJre = true customizedJre = false generateInstaller = true @@ -57,7 +46,6 @@ tasks.register('pack', PackageTask) { winConfig { icoFile = file('src/main/resources/images/icon/ico_eocvsim.ico') - generateMsi = false disableDirPage = false disableProgramGroupPage = false @@ -69,38 +57,62 @@ tasks.register('pack', PackageTask) { } } - tasks.processResources { from({ project(":PaperVisionShadow").tasks.shadowJar }) { - rename { "PaperVisionPlugin.jar" } // name inside resources - into("embedded_plugins") // folder inside resources + rename { "PaperVisionPlugin.jar" } + into("embedded_plugins") } } + +wpilibTools.deps.wpilibVersion = wpilibVersion + +def nativeConfigName = 'wpilibNatives' +def nativeConfig = configurations.create(nativeConfigName) + +def nativeTasks = wpilibTools.createExtractionTasks { + configurationName = nativeConfigName +} + +nativeTasks.addToSourceSetResources(sourceSets.main) + +nativeConfig.dependencies.add wpilibTools.deps.wpilib("wpiutil") +nativeConfig.dependencies.add wpilibTools.deps.wpilib("cscore") +nativeConfig.dependencies.add wpilibTools.deps.wpilibOpenCv(opencvVersion) + dependencies { api project(':Common') api project(':Vision') - implementation 'org.jetbrains.kotlin:kotlin-stdlib:2.3.0' + implementation 'org.jetbrains.kotlin:kotlin-stdlib' implementation(platform("io.insert-koin:koin-bom:$koin_version")) implementation("io.insert-koin:koin-core") implementation "org.eclipse.jdt:ecj:3.21.0" - api "org.openpnp:opencv:$opencv_version" + // WPILib OpenCV replaces openpnp + api wpilibTools.deps.wpilibOpenCvJava(opencvVersion) + wpilibNatives wpilibTools.deps.wpilibOpenCv(opencvVersion) + + // use cscore for webcams + implementation wpilibTools.deps.wpilibJava("cscore") + wpilibNatives wpilibTools.deps.wpilib("cscore") + // cscore depends on these + implementation wpilibTools.deps.wpilibJava("wpinet") + wpilibNatives wpilibTools.deps.wpilib("wpinet") + implementation wpilibTools.deps.wpilibJava("wpiutil") + wpilibNatives wpilibTools.deps.wpilib("wpiutil") implementation "org.slf4j:slf4j-api:$slf4j_version" implementation "org.apache.logging.log4j:log4j-api:$log4j_version" implementation "org.apache.logging.log4j:log4j-core:2.25.0" implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$log4j_version" - implementation "org.deltacv.steve:core:1.1.3" - implementation "org.deltacv.steve:backend-openpnp:1.1.3" - implementation "info.picocli:picocli:$picocli_version" + implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' implementation 'com.google.code.gson:gson:2.8.9' implementation "io.github.classgraph:classgraph:$classgraph_version" @@ -125,8 +137,6 @@ dependencies { implementation 'org.jboss.shrinkwrap.resolver:shrinkwrap-resolver-impl-maven-archive:3.3.2' implementation('org.deltacv.visionloop:streaming:1.2.9') { transitive = false } - - // implementation "org.deltacv.PaperVision:EOCVSimPlugin:$papervision_version" } tasks.register('writeBuildClassJava') { @@ -158,7 +168,7 @@ public final class Build { public static final String buildDate = "$date"; public static final boolean isDev = ${version.contains("dev")}; - public static final String opencvVersion = "$opencv_version"; + public static final String opencvVersion = "$opencvVersion"; public static final String apriltagPluginVersion = "$apriltag_plugin_version"; public static final String paperVisionVersion = "$papervision_version"; } 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 5f6dca89..1652732d 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 @@ -33,12 +33,12 @@ import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.pipeline.PipelineSource import com.github.serivesmejia.eocvsim.tuner.TunerManager import com.github.serivesmejia.eocvsim.util.JavaProcess +import com.github.serivesmejia.eocvsim.util.LibraryLoader import com.github.serivesmejia.eocvsim.util.event.EventHandler -import com.github.serivesmejia.eocvsim.util.orchestration.Orchestrator -import com.github.serivesmejia.eocvsim.util.exception.handling.CrashReport import com.github.serivesmejia.eocvsim.util.exception.handling.EOCVSimUncaughtExceptionHandler import com.github.serivesmejia.eocvsim.util.fps.FpsLimiter import com.github.serivesmejia.eocvsim.util.io.EOCVSimFolder +import com.github.serivesmejia.eocvsim.util.orchestration.Orchestrator import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager import com.qualcomm.robotcore.eventloop.opmode.OpModePipelineHandler import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator @@ -50,11 +50,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.Channel -import nu.pattern.OpenCV import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.koin.core.qualifier.named -import org.opencv.core.Mat import org.opencv.core.Size import org.openftc.easyopencv.TimestampedPipelineHandler import java.io.File @@ -88,48 +86,6 @@ class EOCVSim : KoinComponent { init { EOCVSimFolder.mkdir() // mkdir needed folders } - - private var isNativeLibLoaded = false - - /** - * Load the OpenCV native library - * @param alternativeNative the alternative native library file to load instead of the packaged one - */ - fun loadOpenCvLib(alternativeNative: File? = null) { - if (isNativeLibLoaded) return - - if (alternativeNative != null) { - logger.info("Loading native lib from ${alternativeNative.absolutePath}...") - - try { - System.load(alternativeNative.absolutePath) - - Mat().release() //test if OpenCV is loaded correctly - - isNativeLibLoaded = true - logger.info("Successfully loaded the OpenCV native lib from specified path") - - return - } catch (ex: Throwable) { - logger.error("Failure loading the OpenCV native lib from specified path", ex) - logger.info("Retrying with loadLocally...") - } - } - - try { - OpenCV.loadLocally() - logger.info("Successfully loaded the OpenCV native lib") - } catch (ex: Throwable) { - logger.error("Failure loading the OpenCV native lib", ex) - logger.error("The sim will exit now as it's impossible to continue without OpenCV") - - CrashReport(ex).saveCrashReport() - - exitProcess(-1) - } - - isNativeLibLoaded = true - } } val parameters: Parameters by inject() @@ -244,7 +200,12 @@ class EOCVSim : KoinComponent { EOCVSimUncaughtExceptionHandler.register() //loading native lib only once in the app runtime - loadOpenCvLib(parameters.opencvNativeLibrary) + val loadLibrariesResult = LibraryLoader.loadLibraries() + if(!loadLibrariesResult.success) { + logger.error("Exception in loadLibraries():", loadLibrariesResult.error) + logger.error("The sim will exit now as it's impossible to continue without the required libraries") + exitProcess(-1) + } orchestrator.changePhase(Orchestrator.Phase.INIT) orchestrator.orchestrate() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt index 4538c7a0..3502d9d0 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt @@ -22,89 +22,57 @@ package com.github.serivesmejia.eocvsim.gui.dialog.source -import com.github.serivesmejia.eocvsim.config.ConfigManager +import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.input.InputSourceManager +import com.github.serivesmejia.eocvsim.input.source.CameraSource import com.github.serivesmejia.eocvsim.util.event.EventHandler -import com.github.serivesmejia.eocvsim.gui.Visualizer +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import org.koin.core.qualifier.named -import com.github.serivesmejia.eocvsim.gui.component.input.EnumComboBox -import com.github.serivesmejia.eocvsim.gui.util.WebcamDriver -import com.github.serivesmejia.eocvsim.input.source.CameraSource -import io.github.deltacv.steve.Webcam -import io.github.deltacv.steve.WebcamRotation -import io.github.deltacv.steve.commonResolutions -import io.github.deltacv.steve.opencv.OpenCvWebcam -import io.github.deltacv.steve.opencv.OpenCvWebcamBackend -import io.github.deltacv.steve.openpnp.OpenPnpBackend -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import org.opencv.core.Size +import org.wpilib.vision.camera.UsbCamera +import org.wpilib.vision.camera.UsbCameraInfo import java.awt.* import javax.swing.* -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject class CreateCameraSource : KoinComponent { private val inputSourceManager: InputSourceManager by inject() - private val configManager: ConfigManager by inject() private val onMainUpdate: EventHandler by inject(named("onMainLoop")) - private val visualizer: Visualizer by inject() companion object { const val VISIBLE_CHARACTERS_COMBO_BOX = 22 - private val sizes = mutableMapOf>() + + // common resolutions to offer since cscore doesn't enumerate supported modes + // until the camera is opened + private val commonResolutions = listOf( + Size(640.0, 480.0), + Size(1280.0, 720.0), + Size(1920.0, 1080.0), + Size(320.0, 240.0), + Size(160.0, 120.0) + ) } val createCameraSource = JDialog(visualizer.frame) private val camerasComboBox = JComboBox() private val dimensionsComboBox = JComboBox() - - private val rotationComboBox = EnumComboBox( - "", - WebcamRotation::class.java, - WebcamRotation.entries.toTypedArray(), - WebcamRotation::displayName - ) { WebcamRotation.fromDisplayName(it) ?: WebcamRotation.UPRIGHT } - private val nameTextField = JTextField(15) private val createButton = JButton("Create") - private var webcams = listOf() - private val indexes = mutableMapOf() - private var usingOpenCvDiscovery = false + private var cameraInfos = emptyArray() private var state = State.INITIAL - private enum class State { INITIAL, NO_WEBCAMS, UNSUPPORTED } + private enum class State { INITIAL, NO_WEBCAMS } init { - // Force preferred driver fallback - if (configManager.config.preferredWebcamDriver == WebcamDriver.OpenIMAJ) - configManager.config.preferredWebcamDriver = WebcamDriver.OpenPnp - - val preferredDriver = configManager.config.preferredWebcamDriver - - when (preferredDriver) { - WebcamDriver.OpenPnp -> { - Webcam.backend = OpenPnpBackend - webcams = try { - Webcam.availableWebcams - } catch (t: Throwable) { - t.printStackTrace() - emptyList() - } - } - - WebcamDriver.OpenCV -> { - webcams = emptyList() - Webcam.backend = OpenCvWebcamBackend - usingOpenCvDiscovery = true - } - - else -> webcams = emptyList() + cameraInfos = try { + UsbCamera.enumerateUsbCameras() + } catch (t: Throwable) { + t.printStackTrace() + emptyArray() } createCameraSource.apply { @@ -112,72 +80,52 @@ class CreateCameraSource : KoinComponent { title = "Create camera source" } - // Build UI val contentsPanel = JPanel(GridBagLayout()) val gbc = GridBagConstraints().apply { fill = GridBagConstraints.HORIZONTAL insets = Insets(7, 0, 0, 7) } - // Camera combo box - val idLabel = JLabel("Available cameras: ", JLabel.RIGHT) - if (webcams.isEmpty()) { + if (cameraInfos.isEmpty()) { camerasComboBox.addItem("No Cameras Detected") state = State.NO_WEBCAMS } else { - webcams.forEachIndexed { index, webcam -> - val name = webcam.name.let { + cameraInfos.forEach { info -> + val name = info.name.let { if (it.length > VISIBLE_CHARACTERS_COMBO_BOX) it.take(VISIBLE_CHARACTERS_COMBO_BOX) + "..." else it } - camerasComboBox.addItem(name) - indexes[name] = index - - if (!sizes.containsKey(name)) { - val resolutions = if (webcam is OpenCvWebcam) { - commonResolutions - } else webcam.supportedResolutions.ifEmpty { - println("Webcam $name has no resolutions, skipping") - return@forEachIndexed - } + } - sizes[name] = resolutions - } + commonResolutions.forEach { res -> + dimensionsComboBox.addItem("${res.width.toInt()}x${res.height.toInt()}") } SwingUtilities.invokeLater { camerasComboBox.selectedIndex = 0 } } val fieldsPanel = JPanel(GridBagLayout()).apply { - add(idLabel, gbc) - gbc.gridx = 1; add(camerasComboBox, gbc) + gbc.gridx = 0; gbc.gridy = 0 + add(JLabel("Available cameras: ", JLabel.RIGHT), gbc) + gbc.gridx = 1 + add(camerasComboBox, gbc) - // Name field gbc.gridx = 0; gbc.gridy = 1 add(JLabel("Source name: ", JLabel.RIGHT), gbc) gbc.gridx = 1 nameTextField.text = "CameraSource-${inputSourceManager.sources.size + 1}" - add(nameTextField, gbc) - // Suggested resolution gbc.gridx = 0; gbc.gridy = 2 - add(JLabel("Suggested resolutions: ", JLabel.RIGHT), gbc) + add(JLabel("Resolution: ", JLabel.RIGHT), gbc) gbc.gridx = 1 add(dimensionsComboBox, gbc) - - // Rotation - gbc.gridx = 0; gbc.gridy = 3 - add(JLabel("Camera rotation: ", JLabel.RIGHT), gbc) - gbc.gridx = 1 - add(rotationComboBox.comboBox, gbc) } gbc.gridx = 0; gbc.gridy = 0 contentsPanel.add(fieldsPanel, gbc) - // Bottom buttons val bottomPanel = JPanel(GridBagLayout()) val gbcBtts = GridBagConstraints().apply { insets = Insets(0, 0, 0, 10) } bottomPanel.add(createButton, gbcBtts) @@ -192,11 +140,8 @@ class CreateCameraSource : KoinComponent { contentsPanel.border = BorderFactory.createEmptyBorder(8, 15, 15, 0) createCameraSource.contentPane.add(contentsPanel, BorderLayout.CENTER) - // Event listeners createButton.addActionListener { onCreateButton() } - camerasComboBox.addActionListener { onCameraSelectionChanged() } - nameTextField.document.addDocumentListener(SimpleDocumentListener { updateCreateButton() }) cancelButton.addActionListener { close() } @@ -212,56 +157,33 @@ class CreateCameraSource : KoinComponent { } private fun onCreateButton() { - val webcam = webcams[getSelectedIndex()] - val dim = sizes[camerasComboBox.selectedItem]!![dimensionsComboBox.selectedIndex] - val rotation = rotationComboBox.selectedEnum ?: WebcamRotation.UPRIGHT - - if (usingOpenCvDiscovery) { - val index = if (webcam is OpenCvWebcam) webcam.index else camerasComboBox.selectedIndex - createSource(nameTextField.text, index, dim, rotation) - } else { - createSource(nameTextField.text, webcam.name, dim, rotation) + val index = camerasComboBox.selectedIndex + val info = cameraInfos.getOrNull(index) ?: return + val size = commonResolutions.getOrNull(dimensionsComboBox.selectedIndex) ?: Size(640.0, 480.0) + + onMainUpdate.once { + inputSourceManager.addInputSource( + nameTextField.text, + CameraSource(info.name, size), + true + ) } close() } private fun onCameraSelectionChanged() { - val webcam = webcams.getOrNull(getSelectedIndex()) ?: run { - state = State.UNSUPPORTED - updateState() - return - } - - nameTextField.text = inputSourceManager.tryName(webcam.name) - - dimensionsComboBox.removeAllItems() - - CoroutineScope(Dispatchers.IO).launch { - val webcamSizes = sizes[camerasComboBox.selectedItem] ?: return@launch - SwingUtilities.invokeLater { - dimensionsComboBox.removeAllItems() - webcamSizes.forEach { dimensionsComboBox.addItem("${it.width.toInt()}x${it.height.toInt()}") } - state = State.INITIAL - updateCreateButton() - updateState() - } - } + val info = cameraInfos.getOrNull(camerasComboBox.selectedIndex) ?: return + nameTextField.text = inputSourceManager.tryName(info.name) + updateCreateButton() } private fun updateState() { when (state) { - State.INITIAL -> { - setInteractables(true) - } + State.INITIAL -> setInteractables(true) State.NO_WEBCAMS -> { nameTextField.text = "" setInteractables(false) } - State.UNSUPPORTED -> { - nameTextField.text = "" - setInteractables(false) - camerasComboBox.isEnabled = true - } } } @@ -277,25 +199,11 @@ class CreateCameraSource : KoinComponent { createCameraSource.dispose() } - private fun createSource(name: String, camName: String, size: Size, rotation: WebcamRotation) { - onMainUpdate.once { inputSourceManager.addInputSource(name, CameraSource(camName, size, rotation), true) } - } - - - private fun createSource(name: String, camIndex: Int, size: Size, rotation: WebcamRotation) { - onMainUpdate.once { inputSourceManager.addInputSource(name, CameraSource(camIndex, size, rotation), true) } - } - - private fun updateCreateButton() { createButton.isEnabled = nameTextField.text.trim().isNotEmpty() && !inputSourceManager.isNameInUse(nameTextField.text) } - - private fun getSelectedIndex() = indexes[camerasComboBox.selectedItem] ?: 0 - - // Simple helper for DocumentListener in Kotlin private class SimpleDocumentListener(val onChange: () -> Unit) : javax.swing.event.DocumentListener { override fun insertUpdate(e: javax.swing.event.DocumentEvent) = onChange() override fun removeUpdate(e: javax.swing.event.DocumentEvent) = onChange() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt index 80ff7e53..743ccb4f 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt @@ -23,44 +23,34 @@ package com.github.serivesmejia.eocvsim.input.source -import com.github.serivesmejia.eocvsim.config.ConfigManager -import com.github.serivesmejia.eocvsim.gui.util.WebcamDriver import com.github.serivesmejia.eocvsim.input.InputSource import com.github.serivesmejia.eocvsim.input.InputSourceInitializer -import com.github.serivesmejia.eocvsim.util.StrUtil - import com.google.gson.annotations.Expose -import io.github.deltacv.steve.Webcam -import io.github.deltacv.steve.WebcamRotation -import io.github.deltacv.steve.opencv.OpenCvWebcam -import io.github.deltacv.steve.opencv.OpenCvWebcamBackend -import io.github.deltacv.steve.openpnp.OpenPnpBackend import org.koin.core.component.KoinComponent -import org.koin.core.component.inject import org.opencv.core.Mat import org.opencv.core.Size -import org.opencv.imgproc.Imgproc import org.openftc.easyopencv.MatRecycler import org.slf4j.LoggerFactory +import org.wpilib.vision.camera.CvSink +import org.wpilib.vision.camera.UsbCamera +import org.wpilib.vision.camera.VideoMode import javax.swing.filechooser.FileFilter -class CameraSource : InputSource { +class CameraSource : InputSource, KoinComponent { companion object { - // for global use, -1 means no webcam currently in use @JvmStatic var currentWebcamIndex = -1 } override val hasSlowInitialization: Boolean get() = true - @delegate:Transient - private val configManager: ConfigManager by inject() - @Transient var webcamIndex: Int = 0 @Expose @JvmField var webcamName: String = "" - @Transient private var camera: Webcam? = null + @Transient var camera: UsbCamera? = null + private set + @Transient private var cvSink: CvSink? = null @Transient private var lastFramePaused: MatRecycler.RecyclableMat? = null @Transient private var lastFrame: MatRecycler.RecyclableMat? = null @@ -70,118 +60,81 @@ class CameraSource : InputSource { @Transient var isLegacyByIndex = false @Expose @JvmField @Volatile var size: Size = Size() - @Expose @JvmField @Volatile var rotation: WebcamRotation = WebcamRotation.UPRIGHT - @Transient private var matRecycler = MatRecycler(4) - @Transient private var capTimeNanos: Long = 0 - @Transient private val logger = LoggerFactory.getLogger(javaClass) constructor() : super() { createdOn = System.currentTimeMillis() } - - constructor(webcamName: String?, size: Size?, rotation: WebcamRotation? = WebcamRotation.UPRIGHT) : super() { + constructor(webcamName: String?, size: Size?) : super() { this.webcamName = webcamName ?: "" this.size = size ?: Size() - this.rotation = rotation ?: WebcamRotation.UPRIGHT createdOn = System.currentTimeMillis() } - - constructor(webcamIndex: Int, size: Size?, rotation: WebcamRotation? = WebcamRotation.UPRIGHT) : super() { + constructor(webcamIndex: Int, size: Size?) : super() { this.webcamIndex = webcamIndex this.size = size ?: Size() - this.rotation = rotation ?: WebcamRotation.UPRIGHT isLegacyByIndex = true createdOn = System.currentTimeMillis() } - - - fun getWebcamPropertyControl() = camera?.propertyControl - override fun setSize(size: Size) { this.size = size } override fun getSize(): Size = size - override fun init(): Boolean { if (initialized) return false initialized = true - if (rotation == WebcamRotation.UPRIGHT) rotation = WebcamRotation.UPRIGHT // already defaulted - - - if (webcamName.isNotEmpty()) { - - when (configManager.config.preferredWebcamDriver) { - WebcamDriver.OpenPnp -> Webcam.backend = OpenPnpBackend - WebcamDriver.OpenIMAJ -> { - configManager.config.preferredWebcamDriver = WebcamDriver.OpenPnp - - Webcam.backend = OpenPnpBackend - } - else -> {} - } - - val webcams = Webcam.availableWebcams - var foundWebcam = false - - for (device in webcams) { - val name = device.name - val similarity = StrUtil.similarity(name, webcamName) - - if (name == webcamName || similarity > 0.6) { - logger.info("\"$name\" compared to \"$webcamName\", similarity $similarity") - camera = device - foundWebcam = true - break - } - } - - if (!foundWebcam) { - logger.error("Could not find webcam $webcamName") + val cam = if (webcamName.isNotEmpty()) { + // find by name + val infos = UsbCamera.enumerateUsbCameras() + val info = infos.firstOrNull { it.name == webcamName } + if (info == null) { + logger.error("Could not find webcam \"$webcamName\"") return false } + UsbCamera(webcamName, info.dev) } else { - Webcam.backend = OpenCvWebcamBackend - camera = OpenCvWebcam(webcamIndex, size, rotation) - + UsbCamera("camera$webcamIndex", webcamIndex) } - camera?.resolution = size - camera?.rotation = rotation + camera = cam + if (size.width > 0 && size.height > 0) { + cam.setResolution(size.width.toInt(), size.height.toInt()) + } - try { - camera?.open() - } catch (ex: Exception) { - logger.error("Error while opening camera $webcamIndex", ex) - return false + // pick highest fps mode available at requested resolution + val mode = cam.videoMode + cam.videoMode = VideoMode(mode.pixelFormat, mode.width, mode.height, mode.fps) + + cvSink = CvSink("eocvsim_sink_$webcamIndex").also { + it.source = cam } - if (camera?.isOpen != true) { - logger.error("Unable to open camera $webcamIndex, isOpen() returned false.") + // test frame + val testMat = matRecycler.takeMatOrNull()!! + val grabbed = cvSink!!.grabFrame(testMat) + if (grabbed == 0L) { + logger.error("Unable to open camera $webcamIndex: ${cvSink!!.error}") + testMat.returnMat() return false } - val newFrame = matRecycler.takeMatOrNull() - - camera?.read(newFrame) - - if (newFrame!!.empty()) { + if (testMat.empty()) { logger.error("Unable to open camera $webcamIndex, returned Mat was empty.") - newFrame.release() + testMat.returnMat() return false } - matRecycler.returnMat(newFrame) + matRecycler.returnMat(testMat) currentWebcamIndex = webcamIndex return true @@ -189,7 +142,11 @@ class CameraSource : InputSource { override fun reset() { if (!initialized) return - if (camera?.isOpen == true) camera?.close() + + cvSink?.close() + cvSink = null + camera?.close() + camera = null if (lastFrame?.isCheckedOut == true) lastFrame?.returnMat() if (lastFramePaused?.isCheckedOut == true) lastFramePaused?.returnMat() @@ -198,7 +155,10 @@ class CameraSource : InputSource { } override fun close() { - if (camera?.isOpen == true) camera?.close() + cvSink?.close() + cvSink = null + camera?.close() + camera = null currentWebcamIndex = -1 } @@ -208,75 +168,64 @@ class CameraSource : InputSource { lastNewFrame?.returnMat() lastNewFrame = null - if (isPaused) { - return lastFramePaused - } else if (lastFramePaused != null) { + if (isPaused) return lastFramePaused + + if (lastFramePaused != null) { lastFramePaused?.release() lastFramePaused?.returnMat() lastFramePaused = null } if (lastFrame == null) lastFrame = matRecycler.takeMatOrNull() - if (camera == null) return lastFrame + if (cvSink == null) return lastFrame - val newFrame = matRecycler.takeMatOrNull() + val newFrame = matRecycler.takeMatOrNull()!! lastNewFrame = newFrame - camera?.read(newFrame) + val grabbed = cvSink!!.grabFrameNoTimeout(newFrame) capTimeNanos = System.nanoTime() - if (newFrame == null || newFrame.empty()) { - - newFrame?.returnMat() + if (grabbed == 0L || newFrame.empty()) { + newFrame.returnMat() + lastNewFrame = null return lastFrame } - if (size.area() == 0.0) size = lastFrame!!.size() + if (size.area() == 0.0) size = newFrame.size() newFrame.copyTo(lastFrame) - newFrame.release() newFrame.returnMat() - lastNewFrame = null return lastFrame } override fun onPause() { - if (lastFrame != null) lastFrame?.release() if (lastFramePaused == null) lastFramePaused = matRecycler.takeMatOrNull() + cvSink?.grabFrameNoTimeout(lastFramePaused!!) - camera?.read(lastFramePaused!!) - lastFramePaused?.let { Imgproc.cvtColor(it, it, Imgproc.COLOR_BGR2RGB) } - - update() - + cvSink?.close() + cvSink = null camera?.close() + camera = null currentWebcamIndex = -1 } override fun onResume() { InputSourceInitializer.runWithTimeout(this) { - - camera?.open() - camera?.isOpen == true + init() } } override fun internalCloneSource(): InputSource = if (isLegacyByIndex) { - CameraSource(webcamIndex, size, rotation) + CameraSource(webcamIndex, size) } else { - CameraSource(webcamName, size, rotation) + CameraSource(webcamName, size) } - override val fileFilters: FileFilter? get() = null override val captureTimeNanos: Long get() = capTimeNanos - - override fun toString(): String { - return "CameraSource($webcamName, $webcamIndex, ${size})" - } - -} + override fun toString() = "CameraSource($webcamName, $webcamIndex, $size)" +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/CombinedRuntimeLoader.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/CombinedRuntimeLoader.java new file mode 100644 index 00000000..122f109b --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/CombinedRuntimeLoader.java @@ -0,0 +1,305 @@ +/* + * Copyright (c) FIRST and other WPILib contributors. + * Open Source Software; you can modify and/or share it under the terms of + * the WPILib BSD license below: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * Neither the name of FIRST, WPILib, nor the names of other WPILib + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + */ + +package com.github.serivesmejia.eocvsim.util; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.HexFormat; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; + +/** Loads dynamic libraries for all platforms. */ +public final class CombinedRuntimeLoader { + private CombinedRuntimeLoader() {} + + private static String extractionDirectory; + + private static final Object extractCompleteLock = new Object(); + private static boolean extractAndVerifyComplete = false; + private static List filesAlreadyExtracted = new CopyOnWriteArrayList<>(); + + /** + * Returns library extraction directory. + * + * @return Library extraction directory. + */ + public static synchronized String getExtractionDirectory() { + return extractionDirectory; + } + + private static synchronized void setExtractionDirectory(String directory) { + extractionDirectory = directory; + } + + private static String defaultExtractionRoot; + + /** + * Gets the default extraction root location (~/.wpilib/nativecache) for use if + * setExtractionDirectory is not set. + * + * @return The default extraction root location. + */ + public static synchronized String getDefaultExtractionRoot() { + if (defaultExtractionRoot != null) { + return defaultExtractionRoot; + } + String home = System.getProperty("user.home"); + defaultExtractionRoot = Paths.get(home, ".wpilib", "nativecache").toString(); + return defaultExtractionRoot; + } + + /** + * Returns platform path. + * + * @return The current platform path. + * @throws IllegalStateException Thrown if the operating system is unknown. + */ + public static String getPlatformPath() { + String filePath; + String arch = System.getProperty("os.arch"); + + boolean intel32 = "x86".equals(arch) || "i386".equals(arch); + boolean intel64 = "amd64".equals(arch) || "x86_64".equals(arch); + + if (System.getProperty("os.name").startsWith("Windows")) { + if (intel32) { + filePath = "/windows/x86/"; + } else { + filePath = "/windows/x86-64/"; + } + } else if (System.getProperty("os.name").startsWith("Mac")) { + filePath = "/osx/universal/"; + } else if (System.getProperty("os.name").startsWith("Linux")) { + if (intel32) { + filePath = "/linux/x86/"; + } else if (intel64) { + filePath = "/linux/x86-64/"; + } else if ("arm".equals(arch) || "arm32".equals(arch)) { + filePath = "/linux/arm32/"; + } else if ("aarch64".equals(arch) || "arm64".equals(arch)) { + filePath = "/linux/arm64/"; + } else { + filePath = "/linux/nativearm/"; + } + } else { + throw new IllegalStateException(); + } + + return filePath; + } + + private static String getLoadErrorMessage(String libraryName, UnsatisfiedLinkError ule) { + StringBuilder msg = new StringBuilder(512); + msg.append(libraryName) + .append(" could not be loaded from path\n" + "\tattempted to load for platform ") + .append(getPlatformPath()) + .append("\nLast Load Error: \n") + .append(ule.getMessage()) + .append('\n'); + if (System.getProperty("os.name").startsWith("Windows")) { + msg.append( + "A common cause of this error is missing the C++ runtime.\n" + + "Download the latest at https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads\n"); + } + return msg.toString(); + } + + /** + * Architecture-specific information containing file hashes for a specific CPU architecture (e.g., + * x86-64, arm64). + */ + public record ArchInfo(Map fileHashes) {} + + /** + * Platform-specific information containing architectures for a specific OS platform (e.g., linux, + * windows). + */ + public record PlatformInfo(Map architectures) {} + + /** Overall resource information to be serialized */ + public record ResourceInformation( + // Combined MD5 hash of all native resource files + String hash, + // Platform-specific native libraries organized by platform then architecture + Map platforms, + // List of supported versions for these native resources + List versions) {} + + /** + * Extract a list of native libraries. + * + * @param The class where the resources would be located + * @param clazz The actual class object + * @param resourceName The resource name on the classpath to use for file lookup + * @return List of all libraries that were extracted + * @throws IOException Thrown if resource not found or file could not be extracted + */ + public static List extractLibraries(Class clazz, String resourceName) + throws IOException { + ObjectMapper mapper = new ObjectMapper(); + ResourceInformation resourceInfo; + try (var stream = clazz.getResourceAsStream(resourceName)) { + resourceInfo = mapper.readValue(stream, ResourceInformation.class); + } + + var platformPath = Paths.get(getPlatformPath()); + var platform = platformPath.getName(0).toString(); + var arch = platformPath.getName(1).toString(); + + var platformInfo = resourceInfo.platforms().get(platform); + if (platformInfo == null) { + throw new IOException("Platform " + platform + " not found in resource information"); + } + + var archInfo = platformInfo.architectures().get(arch); + if (archInfo == null) { + throw new IOException("Architecture " + arch + " not found for platform " + platform); + } + + // Map of to + Map filenameToHash = archInfo.fileHashes(); + + var extractionPathString = getExtractionDirectory(); + + if (extractionPathString == null) { + // Folder to extract to derived from overall hash + String combinedHash = resourceInfo.hash(); + + var defaultExtractionRoot = getDefaultExtractionRoot(); + var extractionPath = Paths.get(defaultExtractionRoot, platform, arch, combinedHash); + extractionPathString = extractionPath.toString(); + + setExtractionDirectory(extractionPathString); + } + + List extractedFiles = new ArrayList<>(); + + for (String file : filenameToHash.keySet()) { + try (var stream = clazz.getResourceAsStream(file)) { + Objects.requireNonNull(stream); + + var outputFile = Paths.get(extractionPathString, new File(file).getName()); + + String fileHash = filenameToHash.get(file); + + extractedFiles.add(outputFile.toString()); + if (outputFile.toFile().exists()) { + if (hashEm(outputFile.toFile()).equals(fileHash)) { + continue; + } else { + // Hashes don't match, delete and re-extract + System.err.println( + outputFile.toAbsolutePath().toString() + + " failed validation - deleting and re-extracting"); + outputFile.toFile().delete(); + } + } + var parent = outputFile.getParent(); + if (parent == null) { + throw new IOException("Output file has no parent"); + } + parent.toFile().mkdirs(); + + try (var os = Files.newOutputStream(outputFile)) { + Files.copy(stream, outputFile, StandardCopyOption.REPLACE_EXISTING); + } + + if (!hashEm(outputFile.toFile()).equals(fileHash)) { + throw new IOException("Hash of extracted file does not match expected hash"); + } + } + } + + return extractedFiles; + } + + private static String hashEm(File f) throws IOException { + try { + MessageDigest fileHash = MessageDigest.getInstance("MD5"); + try (var dis = + new DigestInputStream(new BufferedInputStream(new FileInputStream(f)), fileHash)) { + dis.readAllBytes(); + } + var ret = HexFormat.of().formatHex(fileHash.digest()); + return ret; + } catch (NoSuchAlgorithmException e) { + throw new IOException("Unable to verify extracted native files", e); + } + } + + /** + * Load a single library from a list of extracted files. + * + * @param libraryName The library name to load + * @param extractedFiles The extracted files to search + * @throws IOException If library was not found + */ + public static void loadLibrary(String libraryName, List extractedFiles) + throws IOException { + String currentPath = null; + try { + for (var extractedFile : extractedFiles) { + if (extractedFile.contains(libraryName)) { + // Load it + currentPath = extractedFile; + System.load(extractedFile); + return; + } + } + throw new IOException("Could not find library " + libraryName); + } catch (UnsatisfiedLinkError ule) { + throw new IOException(getLoadErrorMessage(currentPath, ule)); + } + } + + /** + * Load a list of native libraries out of a single directory. + * + * @param The class where the resources would be located + * @param clazz The actual class object + * @param librariesToLoad List of libraries to load + * @throws IOException Throws an IOException if not found + */ + public static void loadLibraries(Class clazz, String... librariesToLoad) + throws IOException { + synchronized (extractCompleteLock) { + if (!extractAndVerifyComplete) { + // Extract everything + filesAlreadyExtracted = extractLibraries(clazz, "/ResourceInformation.json"); + extractAndVerifyComplete = true; + } + + for (var library : librariesToLoad) { + loadLibrary(library, filesAlreadyExtracted); + } + } + } +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/LibraryLoader.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/LibraryLoader.java new file mode 100644 index 00000000..97e7cf4c --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/LibraryLoader.java @@ -0,0 +1,36 @@ +package com.github.serivesmejia.eocvsim.util; + +import java.io.IOException; + +import org.opencv.core.Core; +import org.wpilib.net.WPINetJNI; +import org.wpilib.util.WPIUtilJNI; +import org.wpilib.vision.camera.CameraServerJNI; +import org.wpilib.vision.camera.OpenCvLoader; + +public class LibraryLoader { + public record Result(boolean success, IOException error) {} + + public static Result loadLibraries() { + WPIUtilJNI.Helper.setExtractOnStaticLoad(false); + CameraServerJNI.Helper.setExtractOnStaticLoad(false); + OpenCvLoader.Helper.setExtractOnStaticLoad(false); + WPINetJNI.Helper.setExtractOnStaticLoad(false); + + try { + CombinedRuntimeLoader.loadLibraries(LibraryLoader.class, "wpiutiljni"); + WPIUtilJNI.checkMsvcRuntime(); + + CombinedRuntimeLoader.loadLibraries( + LibraryLoader.class, + "wpinetjni", + "cscorejni"); + + CombinedRuntimeLoader.loadLibraries(LibraryLoader.class, Core.NATIVE_LIBRARY_NAME); + } catch(IOException e) { + return new Result(false, e); + } + + return new Result(true, null); + } +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/EOCVSimUncaughtExceptionHandler.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/EOCVSimUncaughtExceptionHandler.kt index 86fc47ec..e1a74b3b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/EOCVSimUncaughtExceptionHandler.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/EOCVSimUncaughtExceptionHandler.kt @@ -33,9 +33,9 @@ class EOCVSimUncaughtExceptionHandler private constructor() : Thread.UncaughtExc logger.error("Uncaught exception thrown in \"${t.name}\" thread", e) - //Exit if uncaught exception happened in the main thread - //since we would be basically in a deadlock state if that happened - //or if we have a lotta uncaught exceptions. + // Exit if uncaught exception happened in the main thread + // since we would be basically in a deadlock state if that happened + // or if we have a lotta uncaught exceptions. if(t == currentMainThread || SwingUtilities.isEventDispatchThread() || e !is Exception || uncaughtExceptionsCount > MAX_UNCAUGHT_EXCEPTIONS_BEFORE_CRASH) { val file = CrashReport(e).saveCrashReport() diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt index caec1a66..0226d295 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt @@ -1,41 +1,94 @@ package io.github.deltacv.eocvsim.input.control import com.github.serivesmejia.eocvsim.input.source.CameraSource -import io.github.deltacv.steve.WebcamProperty import org.firstinspires.ftc.robotcore.external.hardware.camera.controls.ExposureControl import org.firstinspires.ftc.robotcore.internal.collections.MutableReference +import org.wpilib.vision.camera.VideoProperty import java.util.concurrent.TimeUnit class CameraSourceExposureControl( - cameraSource: CameraSource + private val cameraSource: CameraSource ) : ExposureControl { - val control = cameraSource.getWebcamPropertyControl()!! - - override fun getMode() = if(control.getPropertyAuto(WebcamProperty.EXPOSURE)) { - ExposureControl.Mode.Auto - } else ExposureControl.Mode.Manual + private val camera + get() = cameraSource.camera ?: error("Camera not initialized") + + /** + * CSCore exposure property. + * + * Usually: + * - exposure_absolute (Windows / DirectShow) + * - exposure (Linux / V4L) + * + * Try absolute first. + */ + private val exposureProperty: VideoProperty + get() { + val abs = camera.getProperty("exposure_absolute") + + return if (abs.kind != VideoProperty.Kind.kNone) { + abs + } else { + camera.getProperty("exposure") + } + } + + override fun getMode(): ExposureControl.Mode { + val prop = camera.getProperty("exposure_auto") + + if (prop.kind == VideoProperty.Kind.kNone) { + return ExposureControl.Mode.Unknown + } + + return if (prop.get() != 0) { + ExposureControl.Mode.Auto + } else { + ExposureControl.Mode.Manual + } + } override fun setMode(mode: ExposureControl.Mode): Boolean { - control.setPropertyAuto(WebcamProperty.EXPOSURE, mode == ExposureControl.Mode.Auto) - return control.getPropertyAuto(WebcamProperty.EXPOSURE) + when (mode) { + ExposureControl.Mode.Auto -> { + camera.setExposureAuto() + } + + ExposureControl.Mode.Manual -> { + // Keep current exposure value when switching to manual + val current = exposureProperty.get() + camera.setExposureManual(current) + } + + else -> return false + } + + return getMode() == mode } - override fun isModeSupported(mode: ExposureControl.Mode) = - mode == ExposureControl.Mode.Auto || mode == ExposureControl.Mode.Manual + override fun isModeSupported(mode: ExposureControl.Mode): Boolean { + return mode == ExposureControl.Mode.Auto || + mode == ExposureControl.Mode.Manual + } override fun getMinExposure(resultUnit: TimeUnit): Long { - val bounds = control.getPropertyBounds(WebcamProperty.EXPOSURE) - return TimeUnit.SECONDS.convert(bounds.min.toLong(), resultUnit) + return convertExposure( + exposureProperty.min.toLong(), + resultUnit + ) } override fun getMaxExposure(resultUnit: TimeUnit): Long { - val bounds = control.getPropertyBounds(WebcamProperty.EXPOSURE) - return TimeUnit.SECONDS.convert(bounds.max.toLong(), resultUnit) + return convertExposure( + exposureProperty.max.toLong(), + resultUnit + ) } override fun getExposure(resultUnit: TimeUnit): Long { - return TimeUnit.SECONDS.convert(control.getProperty(WebcamProperty.EXPOSURE).toLong(), resultUnit) + return convertExposure( + exposureProperty.get().toLong(), + resultUnit + ) } @Deprecated("") @@ -44,23 +97,58 @@ class CameraSourceExposureControl( refreshed: MutableReference, permittedStaleness: Long, permittedStalenessUnit: TimeUnit - ) = getExposure(resultUnit) + ): Long { + refreshed.value = true + return getExposure(resultUnit) + } - override fun setExposure(duration: Long, durationUnit: TimeUnit): Boolean { - val seconds = TimeUnit.SECONDS.convert(duration, durationUnit).toInt() + override fun setExposure( + duration: Long, + durationUnit: TimeUnit + ): Boolean { - control.setProperty(WebcamProperty.EXPOSURE, seconds) - return control.getProperty(WebcamProperty.EXPOSURE) == seconds + val value = convertFromTimeUnit(duration, durationUnit) + + camera.setExposureManual(value.toInt()) + + return exposureProperty.get() == value.toInt() } - override fun isExposureSupported() = control.isPropertySupported(WebcamProperty.EXPOSURE) + override fun isExposureSupported(): Boolean { + return exposureProperty.kind != VideoProperty.Kind.kNone + } override fun getAePriority(): Boolean { - throw UnsupportedOperationException("AE priority is not supported by EOCV-Sim") + throw UnsupportedOperationException( + "AE priority is not supported by CSCore" + ) } override fun setAePriority(priority: Boolean): Boolean { - throw UnsupportedOperationException("AE priority is not supported by EOCV-Sim") + throw UnsupportedOperationException( + "AE priority is not supported by CSCore" + ) } + /** + * CSCore exposure values are typically milliseconds-ish integers, + * not true nanosecond durations. + * + * FTC ExposureControl expects time units. + * + * You may need platform-specific scaling here. + */ + private fun convertExposure( + value: Long, + resultUnit: TimeUnit + ): Long { + return resultUnit.convert(value, TimeUnit.MILLISECONDS) + } + + private fun convertFromTimeUnit( + duration: Long, + unit: TimeUnit + ): Long { + return TimeUnit.MILLISECONDS.convert(duration, unit) + } } \ No newline at end of file diff --git a/EOCV-Sim/src/test/kotlin/com/github/serivesmejia/eocvsim/test/CoreTests.kt b/EOCV-Sim/src/test/kotlin/com/github/serivesmejia/eocvsim/test/CoreTests.kt index 2b911300..d4e02b0b 100644 --- a/EOCV-Sim/src/test/kotlin/com/github/serivesmejia/eocvsim/test/CoreTests.kt +++ b/EOCV-Sim/src/test/kotlin/com/github/serivesmejia/eocvsim/test/CoreTests.kt @@ -25,13 +25,14 @@ package com.github.serivesmejia.eocvsim.test import com.github.serivesmejia.eocvsim.EOCVSim +import com.github.serivesmejia.eocvsim.util.LibraryLoader import io.kotest.core.spec.style.StringSpec import org.opencv.core.Mat import org.openftc.apriltag.AprilTagDetectorJNI class OpenCvTest : StringSpec({ "Loading native library" { - EOCVSim.loadOpenCvLib() + LibraryLoader.loadLibraries() } "Creating a Mat" { diff --git a/Vision/build.gradle b/Vision/build.gradle index 15eb033a..c223fdf3 100644 --- a/Vision/build.gradle +++ b/Vision/build.gradle @@ -1,11 +1,27 @@ plugins { id 'kotlin' id 'signing' + id 'org.photonvision.tools.WpilibTools' id "com.vanniktech.maven.publish" version "0.30.0" } apply from: '../build.common.gradle' +wpilibTools.deps.wpilibVersion = wpilibVersion + +def nativeConfigName = 'wpilibNatives' +def nativeConfig = configurations.create(nativeConfigName) + +def nativeTasks = wpilibTools.createExtractionTasks { + configurationName = nativeConfigName +} + +nativeTasks.addToSourceSetResources(sourceSets.main) + +nativeConfig.dependencies.add wpilibTools.deps.wpilib("wpiutil") +nativeConfig.dependencies.add wpilibTools.deps.wpilib("cscore") +nativeConfig.dependencies.add wpilibTools.deps.wpilibOpenCv(opencvVersion) + dependencies { implementation project(':Common') @@ -13,18 +29,14 @@ dependencies { transitive = false } - api "org.openpnp:opencv:$opencv_version" + api wpilibTools.deps.wpilibOpenCvJava(opencvVersion) + wpilibNatives wpilibTools.deps.wpilibOpenCv(opencvVersion) implementation "org.slf4j:slf4j-api:$slf4j_version" implementation 'org.jetbrains.kotlin:kotlin-stdlib' - - // Compatibility: Skiko supports many platforms but we will only be adding - // those that are supported by AprilTagDesktop as well - implementation("org.jetbrains.skiko:skiko-awt-runtime-windows-x64:$skiko_version") implementation("org.jetbrains.skiko:skiko-awt-runtime-linux-x64:$skiko_version") - implementation("org.jetbrains.skiko:skiko-awt-runtime-linux-x64:$skiko_version") implementation("org.jetbrains.skiko:skiko-awt-runtime-linux-arm64:$skiko_version") implementation("org.jetbrains.skiko:skiko-awt-runtime-macos-x64:$skiko_version") implementation("org.jetbrains.skiko:skiko-awt-runtime-macos-arm64:$skiko_version") diff --git a/build.common.gradle b/build.common.gradle index bd22d10f..5fe2ac71 100644 --- a/build.common.gradle +++ b/build.common.gradle @@ -1,11 +1,9 @@ java { toolchain { - languageVersion = JavaLanguageVersion.of(17) + languageVersion = JavaLanguageVersion.of(25) } } -import org.gradle.plugins.signing.Sign - tasks.withType(Sign).configureEach { // Disable signing unless explicitly enabled AND not publishing to Maven Local def releaseSigningEnabled = diff --git a/build.gradle b/build.gradle index 2f8c247a..ea750706 100644 --- a/build.gradle +++ b/build.gradle @@ -3,53 +3,61 @@ import java.time.LocalDateTime import java.time.format.DateTimeFormatter buildscript { - ext { - kotlin_version = "2.3.0" - kotlinx_coroutines_version = "1.10.2" - koin_version = "4.2.0" - slf4j_version = "2.0.16" - log4j_version = "2.24.1" - opencv_version = "4.7.0-0" - apriltag_plugin_version = "2.1.0-D" - papervision_version = "1.1.0" - - flatlaf_version = "3.5.1" - miglayout_version = "11.4.2" - 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')) { - throw new GradleException("Invalid env property, must be 'dev' or 'release'") - } - - env = Penv == 'release' ? 'release' : 'dev' - - println("Current build is: $env") - } - repositories { mavenCentral() - gradlePluginPortal() } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:9.3.1" classpath 'io.github.fvarrui:javapackager:1.7.6' } } plugins { id 'java' + id 'org.jetbrains.kotlin.jvm' version '2.3.21' apply false + id 'com.gradleup.shadow' version '9.3.1' apply false + id 'org.photonvision.tools.WpilibTools' version '4.1.1-photon' + id "org.wpilib.WPILibRepositoriesPlugin" version "2027.0.0" +} + +ext { + wpilibVersion = "2027.0.0-alpha-6" + opencvVersion = "2027-4.13.0-3" + + kotlin_version = "2.3.21" + kotlinx_coroutines_version = "1.10.2" + koin_version = "4.2.0" + slf4j_version = "2.0.16" + log4j_version = "2.24.1" + apriltag_plugin_version = "2.1.0-D" + papervision_version = "1.1.0" + + flatlaf_version = "3.5.1" + miglayout_version = "11.4.2" + 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')) { + throw new GradleException("Invalid env property, must be 'dev' or 'release'") + } + env = Penv == 'release' ? 'release' : 'dev' + + println("Current build is: $env") + + wpilibNativeName = wpilibTools.platformMapper.currentPlatform.platformName; + jniPlatform = wpilibTools.platformMapper.wpilibClassifier; + + println("Building for platform " + jniPlatform + " wpilib: " + wpilibNativeName) + println("Using WPILib: " + wpilibVersion) + println("Using OpenCV: " + opencvVersion) } allprojects { - group 'org.deltacv.EOCV-Sim' + group 'org.deltacv.EOCV-Sim' version '4.1.2' apply plugin: 'java' @@ -61,34 +69,31 @@ allprojects { repositories { mavenCentral() mavenLocal() - google() maven { url "https://maven.pkg.jetbrains.space/public/p/compose/dev" } maven { url 'https://artifacts.openmicroscopy.org/artifactory/ome.releases/' } + maven { url "https://maven.photonvision.org/releases" } } + wpilibRepositories.use2027Repos() + wpilibRepositories.addAllReleaseRepositories(project) + tasks.withType(Jar).configureEach { manifest { attributes['Main-Class'] = 'com.github.serivesmejia.eocvsim.Main' } } - if(env == 'dev') { - String date = DateTimeFormatter.ofPattern( - "yyMMdd-HHmm" - ).format(LocalDateTime.now()) - + if (env == 'dev') { + String date = DateTimeFormatter.ofPattern("yyMMdd-HHmm").format(LocalDateTime.now()) String hash = findProperty('hash') version += "-dev-${hash ?: date}" println("Final version of ${project} is $version") - File libsFolder = Paths.get( - projectDir.absolutePath, 'build', 'libs' - ).toFile() - - for(file in libsFolder.listFiles()) { - if(file.name.contains("dev") && file.name.endsWith(".jar")) + File libsFolder = Paths.get(projectDir.absolutePath, 'build', 'libs').toFile() + for (file in libsFolder.listFiles()) { + if (file.name.contains("dev") && file.name.endsWith(".jar")) file.delete() } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 731f0325..3afee195 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle b/settings.gradle index 6c492caf..71144a78 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,8 +1,13 @@ pluginManagement { repositories { maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' } + mavenCentral() - maven { url 'https://plugins.gradle.org/m2/' } + gradlePluginPortal() + + maven { url "https://frcmaven.wpi.edu/artifactory/release" } + maven { url "https://maven.photonvision.org/releases" } + maven { url "https://maven.photonvision.org/snapshots" } } } From 5d6f3d0c400b1fb37b696bf06546fb21b6ebfc9d Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 12 May 2026 03:31:10 -0600 Subject: [PATCH 16/40] Migrate to jackson, get rid of gson --- .../eocvsim/plugin/security/Authority.kt | 6 +- .../restrictions/DynamicCodeRestrictions.kt | 1 - EOCV-Sim/build.gradle | 2 +- .../eocvsim/config/ConfigLoader.java | 19 +- .../gui/dialog/source/CreateCameraSource.kt | 235 +++++++----------- .../serivesmejia/eocvsim/input/InputSource.kt | 17 +- .../eocvsim/input/InputSourceLoader.kt | 34 +-- .../eocvsim/input/source/CameraSource.kt | 189 ++++++-------- .../input/source/CameraSourceAdapter.kt | 24 -- .../eocvsim/input/source/HttpSource.kt | 12 +- .../eocvsim/input/source/ImageSource.kt | 14 +- .../eocvsim/input/source/VideoSource.kt | 14 +- .../plugin/api/impl/InputSourceApisImpl.kt | 4 +- .../util/serialization/JacksonJsonSupport.kt | 56 +++++ .../util/serialization/PolymorphicAdapter.kt | 53 ---- .../util/serialization/VideoModeJackson.kt | 75 ++++++ .../workspace/config/WorkspaceConfigLoader.kt | 10 +- .../input/VisionInputSourceProvider.kt | 74 ++++-- .../security/superaccess/SuperAccessDaemon.kt | 19 +- .../superaccess/SuperAccessDaemonClient.kt | 7 +- 20 files changed, 443 insertions(+), 422 deletions(-) delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSourceAdapter.kt create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/JacksonJsonSupport.kt delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/PolymorphicAdapter.kt create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/VideoModeJackson.kt diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt index 154017ed..be3fd5b6 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt @@ -40,9 +40,9 @@ data class Authority( val publicKey: PublicKey ) -data class MutableAuthority( - var name: String, - var publicKey: ByteArray +class MutableAuthority( + var name: String = "", + var publicKey: ByteArray = byteArrayOf() ) fun Authority.toMutable() = MutableAuthority(name, publicKey.encoded) diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/DynamicCodeRestrictions.kt b/Common/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/DynamicCodeRestrictions.kt index 7278dcb0..6cca7af7 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/DynamicCodeRestrictions.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/DynamicCodeRestrictions.kt @@ -151,7 +151,6 @@ val dynamicCodePackageWhitelist = setOf( // Third-party libs explicitly allowed "com.moandjiezana.toml", "net.lingala.zip4j", - "com.google.gson", "com.google.jimfs", "org.slf4j", "com.apache.logging", diff --git a/EOCV-Sim/build.gradle b/EOCV-Sim/build.gradle index 26469090..ed7367cb 100644 --- a/EOCV-Sim/build.gradle +++ b/EOCV-Sim/build.gradle @@ -113,7 +113,7 @@ dependencies { implementation "info.picocli:picocli:$picocli_version" implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' - implementation 'com.google.code.gson:gson:2.8.9' + implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.15.2' implementation "io.github.classgraph:classgraph:$classgraph_version" implementation "com.formdev:flatlaf:$flatlaf_version" diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigLoader.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigLoader.java index 47ed7fbf..5f95550e 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigLoader.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigLoader.java @@ -24,8 +24,7 @@ package com.github.serivesmejia.eocvsim.config; import com.github.serivesmejia.eocvsim.util.SysUtil; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; +import com.github.serivesmejia.eocvsim.util.serialization.JacksonJsonSupport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -41,8 +40,6 @@ public class ConfigLoader { Logger logger = LoggerFactory.getLogger(getClass()); - static Gson gson = new GsonBuilder().setPrettyPrinting().create(); - public Config loadFromFile(File file) throws FileNotFoundException { if (!file.exists()) throw new FileNotFoundException(); @@ -50,9 +47,9 @@ public Config loadFromFile(File file) throws FileNotFoundException { if (jsonConfig.trim().equals("")) return null; try { - return gson.fromJson(jsonConfig, Config.class); + return JacksonJsonSupport.persistenceMapper.readValue(jsonConfig, Config.class); } catch (Exception ex) { - logger.info("Gson exception while parsing config file", ex); + logger.info("Jackson exception while parsing config file", ex); return null; } } @@ -63,7 +60,15 @@ public Config loadFromFile() throws FileNotFoundException { } public void saveToFile(File file, Config conf) { - String jsonConfig = gson.toJson(conf); + String jsonConfig; + + try { + jsonConfig = JacksonJsonSupport.persistenceMapper.writeValueAsString(conf); + } catch (Exception ex) { + logger.error("Failed to serialize config file", ex); + return; + } + SysUtil.saveFileStr(file, jsonConfig); } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt index 3502d9d0..4d07fc90 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt @@ -1,25 +1,3 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - package com.github.serivesmejia.eocvsim.gui.dialog.source import com.github.serivesmejia.eocvsim.gui.Visualizer @@ -29,9 +7,9 @@ import com.github.serivesmejia.eocvsim.util.event.EventHandler import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.koin.core.qualifier.named -import org.opencv.core.Size import org.wpilib.vision.camera.UsbCamera import org.wpilib.vision.camera.UsbCameraInfo +import org.wpilib.vision.camera.VideoMode import java.awt.* import javax.swing.* @@ -41,33 +19,18 @@ class CreateCameraSource : KoinComponent { private val onMainUpdate: EventHandler by inject(named("onMainLoop")) private val visualizer: Visualizer by inject() - companion object { - const val VISIBLE_CHARACTERS_COMBO_BOX = 22 - - // common resolutions to offer since cscore doesn't enumerate supported modes - // until the camera is opened - private val commonResolutions = listOf( - Size(640.0, 480.0), - Size(1280.0, 720.0), - Size(1920.0, 1080.0), - Size(320.0, 240.0), - Size(160.0, 120.0) - ) - } - - val createCameraSource = JDialog(visualizer.frame) - private val camerasComboBox = JComboBox() - private val dimensionsComboBox = JComboBox() + private val modesComboBox = JComboBox() private val nameTextField = JTextField(15) private val createButton = JButton("Create") - private var cameraInfos = emptyArray() + private val dialog = JDialog(visualizer.frame) - private var state = State.INITIAL - private enum class State { INITIAL, NO_WEBCAMS } + private var cameraInfos: Array = emptyArray() + private var modes: Array = emptyArray() init { + cameraInfos = try { UsbCamera.enumerateUsbCameras() } catch (t: Throwable) { @@ -75,138 +38,130 @@ class CreateCameraSource : KoinComponent { emptyArray() } - createCameraSource.apply { + dialog.apply { isModal = true - title = "Create camera source" + title = "Create Camera Source" } - val contentsPanel = JPanel(GridBagLayout()) + val root = JPanel(GridBagLayout()) val gbc = GridBagConstraints().apply { fill = GridBagConstraints.HORIZONTAL - insets = Insets(7, 0, 0, 7) + insets = Insets(5, 5, 5, 5) } if (cameraInfos.isEmpty()) { - camerasComboBox.addItem("No Cameras Detected") - state = State.NO_WEBCAMS + camerasComboBox.addItem("No Cameras Found") } else { - cameraInfos.forEach { info -> - val name = info.name.let { - if (it.length > VISIBLE_CHARACTERS_COMBO_BOX) it.take(VISIBLE_CHARACTERS_COMBO_BOX) + "..." - else it - } - camerasComboBox.addItem(name) - } - - commonResolutions.forEach { res -> - dimensionsComboBox.addItem("${res.width.toInt()}x${res.height.toInt()}") + cameraInfos.forEach { + camerasComboBox.addItem(it.name) } + camerasComboBox.selectedIndex = 0 + loadModes(0) + } - SwingUtilities.invokeLater { camerasComboBox.selectedIndex = 0 } + camerasComboBox.addActionListener { + loadModes(camerasComboBox.selectedIndex) } - val fieldsPanel = JPanel(GridBagLayout()).apply { - gbc.gridx = 0; gbc.gridy = 0 - add(JLabel("Available cameras: ", JLabel.RIGHT), gbc) - gbc.gridx = 1 - add(camerasComboBox, gbc) - - gbc.gridx = 0; gbc.gridy = 1 - add(JLabel("Source name: ", JLabel.RIGHT), gbc) - gbc.gridx = 1 - nameTextField.text = "CameraSource-${inputSourceManager.sources.size + 1}" - add(nameTextField, gbc) - - gbc.gridx = 0; gbc.gridy = 2 - add(JLabel("Resolution: ", JLabel.RIGHT), gbc) - gbc.gridx = 1 - add(dimensionsComboBox, gbc) + modesComboBox.renderer = object : DefaultListCellRenderer() { + override fun getListCellRendererComponent( + list: JList<*>, + value: Any?, + index: Int, + isSelected: Boolean, + cellHasFocus: Boolean + ): Component { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus) + + val mode = value as? VideoMode + + text = if (mode != null) { + "${mode.width}x${mode.height} @ ${mode.fps}fps (${mode.pixelFormat})" + } else { + "Unknown" + } + + return this + } } gbc.gridx = 0; gbc.gridy = 0 - contentsPanel.add(fieldsPanel, gbc) - - val bottomPanel = JPanel(GridBagLayout()) - val gbcBtts = GridBagConstraints().apply { insets = Insets(0, 0, 0, 10) } - bottomPanel.add(createButton, gbcBtts) - gbcBtts.gridx = 1; gbcBtts.insets = Insets(0, 0, 0, 0) - val cancelButton = JButton("Cancel") - bottomPanel.add(cancelButton, gbcBtts) + root.add(JLabel("Camera:"), gbc) + gbc.gridx = 1 + root.add(camerasComboBox, gbc) - gbc.insets = Insets(10, 0, 0, 0) gbc.gridx = 0; gbc.gridy = 1 - contentsPanel.add(bottomPanel, gbc) + root.add(JLabel("Mode:"), gbc) + gbc.gridx = 1 + root.add(modesComboBox, gbc) + + gbc.gridx = 0; gbc.gridy = 2 + root.add(JLabel("Name:"), gbc) + gbc.gridx = 1 + nameTextField.text = "CameraSource-${inputSourceManager.sources.size + 1}" + root.add(nameTextField, gbc) + + val buttons = JPanel() + buttons.add(createButton) + val cancel = JButton("Cancel") + buttons.add(cancel) + + gbc.gridx = 0; gbc.gridy = 3 + gbc.gridwidth = 2 + root.add(buttons, gbc) + + createButton.addActionListener { create() } + cancel.addActionListener { close() } + + dialog.contentPane.add(root) + dialog.pack() + dialog.setLocationRelativeTo(null) + dialog.isVisible = true + } - contentsPanel.border = BorderFactory.createEmptyBorder(8, 15, 15, 0) - createCameraSource.contentPane.add(contentsPanel, BorderLayout.CENTER) + private fun loadModes(index: Int) { + val info = cameraInfos.getOrNull(index) ?: return - createButton.addActionListener { onCreateButton() } - camerasComboBox.addActionListener { onCameraSelectionChanged() } - nameTextField.document.addDocumentListener(SimpleDocumentListener { updateCreateButton() }) - cancelButton.addActionListener { close() } + try { + val cam = UsbCamera(info.name, info.dev) - updateState() + modes = cam.enumerateVideoModes() - createCameraSource.apply { - pack() - isResizable = false - isAlwaysOnTop = true - setLocationRelativeTo(null) - isVisible = true + cam.close() + } catch (t: Throwable) { + t.printStackTrace() + modes = emptyArray() + } + + modesComboBox.removeAllItems() + + for (m in modes) { + modesComboBox.addItem(m) + } + + if (modes.isNotEmpty()) { + modesComboBox.selectedIndex = 0 } } - private fun onCreateButton() { - val index = camerasComboBox.selectedIndex - val info = cameraInfos.getOrNull(index) ?: return - val size = commonResolutions.getOrNull(dimensionsComboBox.selectedIndex) ?: Size(640.0, 480.0) + private fun create() { + val camIndex = camerasComboBox.selectedIndex + val info = cameraInfos.getOrNull(camIndex) ?: return + + val mode = modes.getOrNull(modesComboBox.selectedIndex) ?: return onMainUpdate.once { inputSourceManager.addInputSource( nameTextField.text, - CameraSource(info.name, size), + CameraSource(info.name, mode), true ) } - close() - } - - private fun onCameraSelectionChanged() { - val info = cameraInfos.getOrNull(camerasComboBox.selectedIndex) ?: return - nameTextField.text = inputSourceManager.tryName(info.name) - updateCreateButton() - } - - private fun updateState() { - when (state) { - State.INITIAL -> setInteractables(true) - State.NO_WEBCAMS -> { - nameTextField.text = "" - setInteractables(false) - } - } - } - private fun setInteractables(enabled: Boolean) { - createButton.isEnabled = enabled - nameTextField.isEnabled = enabled - camerasComboBox.isEnabled = enabled - dimensionsComboBox.isEnabled = enabled + close() } private fun close() { - createCameraSource.isVisible = false - createCameraSource.dispose() - } - - private fun updateCreateButton() { - createButton.isEnabled = nameTextField.text.trim().isNotEmpty() && - !inputSourceManager.isNameInUse(nameTextField.text) - } - - private class SimpleDocumentListener(val onChange: () -> Unit) : javax.swing.event.DocumentListener { - override fun insertUpdate(e: javax.swing.event.DocumentEvent) = onChange() - override fun removeUpdate(e: javax.swing.event.DocumentEvent) = onChange() - override fun changedUpdate(e: javax.swing.event.DocumentEvent) = onChange() + dialog.dispose() } } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt index 880c2f0a..e7fa18d8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt @@ -23,17 +23,24 @@ package com.github.serivesmejia.eocvsim.input -import com.google.gson.annotations.Expose +import com.fasterxml.jackson.annotation.JsonAutoDetect +import com.fasterxml.jackson.annotation.JsonProperty import org.koin.core.component.KoinComponent import org.opencv.core.Mat import org.opencv.core.Size import javax.swing.filechooser.FileFilter +@JsonAutoDetect( + fieldVisibility = JsonAutoDetect.Visibility.NONE, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE, + setterVisibility = JsonAutoDetect.Visibility.NONE, + creatorVisibility = JsonAutoDetect.Visibility.NONE +) abstract class InputSource : Comparable, KoinComponent { - // Computed property (no backing field) to avoid adding serializable fields that - // cause duplicate JSON field errors with Gson when subclasses also declare - // a backing field. Subclasses should override with a getter as well. + // Computed property (no backing field) so Jackson field-based persistence only + // stores the actual source state and does not capture derived values. open val hasSlowInitialization: Boolean get() = false @Transient var isDefault = false @@ -50,7 +57,7 @@ abstract class InputSource : Comparable, KoinComponent { beforeIsPaused = value } - @Expose + @JsonProperty @JvmField var createdOn = -1L diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceLoader.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceLoader.kt index b70b1773..63facd3c 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceLoader.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceLoader.kt @@ -23,11 +23,11 @@ package com.github.serivesmejia.eocvsim.input +import com.fasterxml.jackson.annotation.JsonAutoDetect +import com.fasterxml.jackson.annotation.JsonProperty import com.github.serivesmejia.eocvsim.input.source.* import com.github.serivesmejia.eocvsim.util.SysUtil -import com.google.gson.Gson -import com.google.gson.GsonBuilder -import com.google.gson.annotations.Expose +import com.github.serivesmejia.eocvsim.util.serialization.JacksonJsonSupport import org.slf4j.LoggerFactory import java.io.File @@ -36,12 +36,6 @@ class InputSourceLoader { private val logger = LoggerFactory.getLogger(javaClass) companion object { - @JvmStatic - val gson: Gson = GsonBuilder() - .registerTypeAdapter(CameraSource::class.java, CameraSourceAdapter()) - .setPrettyPrinting() - .create() - const val SOURCES_SAVEFILE_NAME = "eocvsim_sources.json" @JvmStatic @@ -89,7 +83,7 @@ class InputSourceLoader { } fun saveInputSourcesToFile(file: File, sourcesContainer: InputSourcesContainer) { - val jsonInputSources = gson.toJson(sourcesContainer) + val jsonInputSources = JacksonJsonSupport.persistenceMapper.writeValueAsString(sourcesContainer) SysUtil.saveFileStr(file, jsonInputSources) } @@ -110,7 +104,7 @@ class InputSourceLoader { val sources: InputSourcesContainer try { - sources = gson.fromJson(jsonSources, InputSourcesContainer::class.java) + sources = JacksonJsonSupport.persistenceMapper.readValue(jsonSources, InputSourcesContainer::class.java) } catch (ex: Exception) { logger.error("Error while parsing sources file, it will be replaced and fixed later on, but the user created sources will be deleted.", ex) return @@ -126,17 +120,23 @@ class InputSourceLoader { loadedInputSources = sources.allSources } + @JsonAutoDetect( + fieldVisibility = JsonAutoDetect.Visibility.NONE, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE, + setterVisibility = JsonAutoDetect.Visibility.NONE, + creatorVisibility = JsonAutoDetect.Visibility.NONE + ) class InputSourcesContainer { @Transient var allSources = HashMap() - var imageSources = HashMap() - var cameraSources = HashMap() - var videoSources = HashMap() - var httpSources = HashMap() + @JsonProperty var imageSources = HashMap() + @JsonProperty var cameraSources = HashMap() + @JsonProperty var videoSources = HashMap() + @JsonProperty var httpSources = HashMap() - @Expose - var sourcesFileVersion: SourcesFileVersion? = null + @JsonProperty var sourcesFileVersion: SourcesFileVersion? = null enum class SourcesFileVersion { DOS, SEIS, SIETE } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt index 743ccb4f..34144d11 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt @@ -1,41 +1,28 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - package com.github.serivesmejia.eocvsim.input.source +import com.fasterxml.jackson.annotation.JsonAutoDetect +import com.fasterxml.jackson.annotation.JsonProperty import com.github.serivesmejia.eocvsim.input.InputSource import com.github.serivesmejia.eocvsim.input.InputSourceInitializer -import com.google.gson.annotations.Expose +import io.github.deltacv.common.util.loggerForThis import org.koin.core.component.KoinComponent import org.opencv.core.Mat import org.opencv.core.Size +import org.opencv.imgproc.Imgproc import org.openftc.easyopencv.MatRecycler -import org.slf4j.LoggerFactory +import org.wpilib.util.PixelFormat import org.wpilib.vision.camera.CvSink import org.wpilib.vision.camera.UsbCamera import org.wpilib.vision.camera.VideoMode import javax.swing.filechooser.FileFilter +@JsonAutoDetect( + fieldVisibility = JsonAutoDetect.Visibility.NONE, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE, + setterVisibility = JsonAutoDetect.Visibility.NONE, + creatorVisibility = JsonAutoDetect.Visibility.NONE +) class CameraSource : InputSource, KoinComponent { companion object { @@ -45,98 +32,95 @@ class CameraSource : InputSource, KoinComponent { override val hasSlowInitialization: Boolean get() = true @Transient var webcamIndex: Int = 0 + @JsonProperty @JvmField var webcamName: String = "" - @Expose @JvmField var webcamName: String = "" + @JsonProperty @JvmField var videoMode: VideoMode? = null @Transient var camera: UsbCamera? = null private set + @Transient private var cvSink: CvSink? = null - @Transient private var lastFramePaused: MatRecycler.RecyclableMat? = null - @Transient private var lastFrame: MatRecycler.RecyclableMat? = null + @Transient private var lastFrame = Mat() @Transient private var initialized = false @Transient var isLegacyByIndex = false - @Expose @JvmField @Volatile var size: Size = Size() - - @Transient private var matRecycler = MatRecycler(4) + @Transient private var matRecycler = MatRecycler(3) @Transient private var capTimeNanos: Long = 0 - @Transient private val logger = LoggerFactory.getLogger(javaClass) + private val logger by loggerForThis() - constructor() : super() { - createdOn = System.currentTimeMillis() - } + constructor() : super() - constructor(webcamName: String?, size: Size?) : super() { + constructor(webcamName: String?, videoMode: VideoMode?) : super() { this.webcamName = webcamName ?: "" - this.size = size ?: Size() - createdOn = System.currentTimeMillis() + this.videoMode = videoMode } - constructor(webcamIndex: Int, size: Size?) : super() { + constructor(webcamIndex: Int, videoMode: VideoMode?) : super() { this.webcamIndex = webcamIndex - this.size = size ?: Size() - isLegacyByIndex = true - createdOn = System.currentTimeMillis() + this.videoMode = videoMode + this.isLegacyByIndex = true } override fun setSize(size: Size) { - this.size = size + // deprecated concept now; derived from VideoMode } - override fun getSize(): Size = size + override fun getSize(): Size = + videoMode?.let { Size(it.width.toDouble(), it.height.toDouble()) } ?: Size() override fun init(): Boolean { if (initialized) return false initialized = true val cam = if (webcamName.isNotEmpty()) { - // find by name val infos = UsbCamera.enumerateUsbCameras() val info = infos.firstOrNull { it.name == webcamName } - if (info == null) { - logger.error("Could not find webcam \"$webcamName\"") - return false - } + ?: run { + logger.error("Camera not found: $webcamName") + return false + } + UsbCamera(webcamName, info.dev) } else { - UsbCamera("camera$webcamIndex", webcamIndex) + UsbCamera("$webcamIndex", webcamIndex) } camera = cam - if (size.width > 0 && size.height > 0) { - cam.setResolution(size.width.toInt(), size.height.toInt()) + val desiredMode = videoMode + + if (desiredMode != null) { + cam.videoMode = desiredMode + } else { + val mode = cam.videoMode + cam.videoMode = mode } - // pick highest fps mode available at requested resolution val mode = cam.videoMode - cam.videoMode = VideoMode(mode.pixelFormat, mode.width, mode.height, mode.fps) - cvSink = CvSink("eocvsim_sink_$webcamIndex").also { + logger.info( + "Camera started: $webcamName ${mode?.stringify()}" + ) + + cvSink = CvSink("eocvsim_sink_$webcamIndex", PixelFormat.BGR).also { it.source = cam } - // test frame - val testMat = matRecycler.takeMatOrNull()!! - val grabbed = cvSink!!.grabFrame(testMat) - if (grabbed == 0L) { - logger.error("Unable to open camera $webcamIndex: ${cvSink!!.error}") - testMat.returnMat() - return false - } + val test = matRecycler.takeMatOrNull() ?: return false + val ok = cvSink!!.grabFrame(test, 5.0) - if (testMat.empty()) { - logger.error("Unable to open camera $webcamIndex, returned Mat was empty.") - testMat.returnMat() + if (ok == 0L || test.empty()) { + test.returnMat() + logger.error("Failed to open camera: ${cvSink!!.error}") return false } - matRecycler.returnMat(testMat) - currentWebcamIndex = webcamIndex + test.returnMat() + currentWebcamIndex = webcamIndex return true } @@ -145,87 +129,58 @@ class CameraSource : InputSource, KoinComponent { cvSink?.close() cvSink = null + camera?.close() camera = null - if (lastFrame?.isCheckedOut == true) lastFrame?.returnMat() - if (lastFramePaused?.isCheckedOut == true) lastFramePaused?.returnMat() + lastFrame.release() initialized = false } override fun close() { cvSink?.close() - cvSink = null camera?.close() - camera = null currentWebcamIndex = -1 } - @Transient private var lastNewFrame: MatRecycler.RecyclableMat? = null - - override fun update(): Mat? { - lastNewFrame?.returnMat() - lastNewFrame = null + override fun update(): Mat { + if (isPaused) return lastFrame - if (isPaused) return lastFramePaused + val grabTime = cvSink?.grabFrame(lastFrame, 3.0) ?: 0L - if (lastFramePaused != null) { - lastFramePaused?.release() - lastFramePaused?.returnMat() - lastFramePaused = null - } - - if (lastFrame == null) lastFrame = matRecycler.takeMatOrNull() - if (cvSink == null) return lastFrame - - val newFrame = matRecycler.takeMatOrNull()!! - lastNewFrame = newFrame - - val grabbed = cvSink!!.grabFrameNoTimeout(newFrame) - capTimeNanos = System.nanoTime() - - if (grabbed == 0L || newFrame.empty()) { - newFrame.returnMat() - lastNewFrame = null + if(lastFrame.empty()) { return lastFrame } - if (size.area() == 0.0) size = newFrame.size() - - newFrame.copyTo(lastFrame) - newFrame.release() - newFrame.returnMat() - lastNewFrame = null - + capTimeNanos = grabTime + Imgproc.cvtColor(lastFrame, lastFrame, Imgproc.COLOR_BGR2RGBA) return lastFrame } override fun onPause() { - if (lastFramePaused == null) lastFramePaused = matRecycler.takeMatOrNull() - cvSink?.grabFrameNoTimeout(lastFramePaused!!) - + cvSink?.grabFrame(lastFrame, 2.0) cvSink?.close() - cvSink = null camera?.close() - camera = null currentWebcamIndex = -1 } override fun onResume() { - InputSourceInitializer.runWithTimeout(this) { - init() - } + InputSourceInitializer.runWithTimeout(this) { init() } } - override fun internalCloneSource(): InputSource = if (isLegacyByIndex) { - CameraSource(webcamIndex, size) - } else { - CameraSource(webcamName, size) - } + override fun internalCloneSource(): InputSource = + if (isLegacyByIndex) { + CameraSource(webcamIndex, videoMode) + } else { + CameraSource(webcamName, videoMode) + } override val fileFilters: FileFilter? get() = null override val captureTimeNanos: Long get() = capTimeNanos - override fun toString() = "CameraSource($webcamName, $webcamIndex, $size)" + override fun toString() = + "CameraSource($webcamName, $webcamIndex, ${videoMode?.stringify()})" + + private fun VideoMode.stringify() = "${width}x${height}@${fps} ${pixelFormat}" } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSourceAdapter.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSourceAdapter.kt deleted file mode 100644 index 103b4581..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSourceAdapter.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.github.serivesmejia.eocvsim.input.source - -import com.google.gson.* -import java.lang.reflect.Type - -class CameraSourceAdapter : JsonSerializer { - - override fun serialize(src: CameraSource, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { - val obj = JsonObject() - - if (src.webcamName.isNotEmpty()) { - obj.addProperty("webcamName", src.webcamName) - } else { - obj.addProperty("webcamIndex", src.webcamIndex) - } - - obj.add("size", context.serialize(src.size)) - obj.addProperty("createdOn", src.creationTime) - - - return obj - } - -} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt index b65dda4b..1d5adc8d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt @@ -23,10 +23,11 @@ package com.github.serivesmejia.eocvsim.input.source +import com.fasterxml.jackson.annotation.JsonAutoDetect +import com.fasterxml.jackson.annotation.JsonProperty import com.github.serivesmejia.eocvsim.input.InputSource import com.github.serivesmejia.eocvsim.input.InputSourceInitializer -import com.google.gson.annotations.Expose import io.github.deltacv.visionloop.io.MjpegHttpReader import org.opencv.core.Mat import org.opencv.core.MatOfByte @@ -35,8 +36,15 @@ import org.opencv.imgproc.Imgproc import org.slf4j.LoggerFactory import javax.swing.filechooser.FileFilter +@JsonAutoDetect( + fieldVisibility = JsonAutoDetect.Visibility.NONE, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE, + setterVisibility = JsonAutoDetect.Visibility.NONE, + creatorVisibility = JsonAutoDetect.Visibility.NONE +) class HttpSource @JvmOverloads constructor( - @Expose @JvmField var url: String = "" + @JsonProperty @JvmField var url: String = "" ) : InputSource() { override val hasSlowInitialization: Boolean get() = true diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.kt index 3fdae0b7..cc5e54c4 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.kt @@ -23,9 +23,10 @@ package com.github.serivesmejia.eocvsim.input.source +import com.fasterxml.jackson.annotation.JsonAutoDetect +import com.fasterxml.jackson.annotation.JsonProperty import com.github.serivesmejia.eocvsim.input.InputSource import com.github.serivesmejia.eocvsim.util.FileFilters -import com.google.gson.annotations.Expose import org.opencv.core.Mat import org.opencv.core.Size import org.opencv.imgcodecs.Imgcodecs @@ -33,9 +34,16 @@ import org.opencv.imgproc.Imgproc import org.openftc.easyopencv.MatRecycler import javax.swing.filechooser.FileFilter +@JsonAutoDetect( + fieldVisibility = JsonAutoDetect.Visibility.NONE, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE, + setterVisibility = JsonAutoDetect.Visibility.NONE, + creatorVisibility = JsonAutoDetect.Visibility.NONE +) class ImageSource @JvmOverloads constructor( - @Expose @JvmField var imgPath: String = "", - @Expose @JvmField var size: Size = Size() + @JsonProperty @JvmField var imgPath: String = "", + @JsonProperty @JvmField var size: Size = Size() ) : InputSource() { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt index e847bfd4..1d320b37 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt @@ -23,10 +23,11 @@ package com.github.serivesmejia.eocvsim.input.source +import com.fasterxml.jackson.annotation.JsonAutoDetect +import com.fasterxml.jackson.annotation.JsonProperty import com.github.serivesmejia.eocvsim.input.InputSource import com.github.serivesmejia.eocvsim.util.FileFilters import com.github.serivesmejia.eocvsim.util.fps.FpsLimiter -import com.google.gson.annotations.Expose import org.opencv.core.Mat import org.opencv.core.Size import org.opencv.imgproc.Imgproc @@ -36,9 +37,16 @@ import org.openftc.easyopencv.MatRecycler import org.slf4j.LoggerFactory import javax.swing.filechooser.FileFilter +@JsonAutoDetect( + fieldVisibility = JsonAutoDetect.Visibility.NONE, + getterVisibility = JsonAutoDetect.Visibility.NONE, + isGetterVisibility = JsonAutoDetect.Visibility.NONE, + setterVisibility = JsonAutoDetect.Visibility.NONE, + creatorVisibility = JsonAutoDetect.Visibility.NONE +) class VideoSource @JvmOverloads constructor( - @Expose @JvmField var videoPath: String = "", - @Expose @JvmField var size: Size = Size() + @JsonProperty @JvmField var videoPath: String = "", + @JsonProperty @JvmField var size: Size = Size() ) : InputSource() { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt index af7ca257..419cd8ed 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt @@ -37,7 +37,7 @@ class InputSourceApiImpl(owner: EOCVSimPlugin, val internalInputSource: com.gith override val data by apiField { when(internalInputSource) { - is CameraSource -> New.Camera(internalInputSource.webcamName, internalInputSource.size) + is CameraSource -> TODO() // New.Camera(internalInputSource.webcamName, internalInputSource.size) is ImageSource -> New.Image(internalInputSource.imgPath, internalInputSource.size) is VideoSource -> New.Video(internalInputSource.videoPath, internalInputSource.size) is HttpSource -> New.Http(internalInputSource.url) @@ -69,7 +69,7 @@ class InputSourceManagerApiImpl(owner: EOCVSimPlugin, val internalInputSourceMan override fun addInputSource(name: String, aNew: InputSourceApi.New) = apiImpl { val source = when(aNew) { - is InputSourceApi.New.Camera -> CameraSource(aNew.cameraName, aNew.size) + is InputSourceApi.New.Camera -> TODO() // CameraSource(aNew.cameraName, aNew.size) is InputSourceApi.New.Image -> ImageSource(aNew.filePath, aNew.size) is InputSourceApi.New.Video -> VideoSource(aNew.filePath, aNew.size) is InputSourceApi.New.Http -> HttpSource(aNew.url) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/JacksonJsonSupport.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/JacksonJsonSupport.kt new file mode 100644 index 00000000..d1c7ea11 --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/JacksonJsonSupport.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 Sebastian Erives + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package com.github.serivesmejia.eocvsim.util.serialization + +import com.fasterxml.jackson.annotation.JsonAutoDetect +import com.fasterxml.jackson.annotation.PropertyAccessor +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.module.kotlin.registerKotlinModule + +/** + * Shared Jackson mappers for the simulator. + * + * `persistenceMapper` is configured for the config/input-source files and only + * serializes fields, avoiding getter-based schema drift from computed properties. + * `ipcMapper` keeps the same Kotlin support but is meant for internal message payloads. + */ +object JacksonJsonSupport { + + @JvmField + val persistenceMapper: ObjectMapper = createMapper() + + @JvmField + val ipcMapper: ObjectMapper = createMapper() + + private fun createMapper(): ObjectMapper = ObjectMapper() + .registerKotlinModule() + .registerModule(VideoModeJackson.module()) + .setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE) + .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .enable(SerializationFeature.INDENT_OUTPUT) + +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/PolymorphicAdapter.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/PolymorphicAdapter.kt deleted file mode 100644 index be150491..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/PolymorphicAdapter.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - -package com.github.serivesmejia.eocvsim.util.serialization - -import com.google.gson.* -import java.lang.reflect.Type -private val gson = Gson() - -open class PolymorphicAdapter( - val name: String, - val classloader: ClassLoader = PolymorphicAdapter::class.java.classLoader -) : JsonSerializer, JsonDeserializer { - - override fun serialize(src: T, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { - val obj = JsonObject() - - obj.addProperty("${name}Class", src!!::class.java.name) - obj.add(name, gson.toJsonTree(src)) - - return obj - } - - @Suppress("UNCHECKED_CAST") - override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): T { - val className = json.asJsonObject.get("${name}Class").asString - - val clazz = classloader.loadClass(className) - - return gson.fromJson(json.asJsonObject.get(name), clazz) as T - } - -} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/VideoModeJackson.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/VideoModeJackson.kt new file mode 100644 index 00000000..8231892b --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/VideoModeJackson.kt @@ -0,0 +1,75 @@ +package com.github.serivesmejia.eocvsim.util.serialization + +import com.fasterxml.jackson.core.JsonGenerator +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.JsonSerializer +import com.fasterxml.jackson.databind.SerializerProvider +import com.fasterxml.jackson.databind.module.SimpleModule +import org.wpilib.util.PixelFormat +import org.wpilib.vision.camera.VideoMode + +/** + * Keeps persisted CameraSource.videoMode JSON stable while allowing reconstruction + * of WPILib VideoMode, which has no Jackson-friendly default constructor. + */ +object VideoModeJackson { + + fun module(): SimpleModule = SimpleModule("VideoModeModule") + .addSerializer(VideoMode::class.java, VideoModeSerializer) + .addDeserializer(VideoMode::class.java, VideoModeDeserializer) + + private object VideoModeSerializer : JsonSerializer() { + override fun serialize(value: VideoMode, gen: JsonGenerator, serializers: SerializerProvider) { + gen.writeStartObject() + gen.writeStringField("pixelFormat", value.pixelFormat.toString()) + gen.writeNumberField("width", value.width) + gen.writeNumberField("height", value.height) + gen.writeNumberField("fps", value.fps) + gen.writeEndObject() + } + } + + private object VideoModeDeserializer : JsonDeserializer() { + override fun deserialize(parser: JsonParser, ctxt: DeserializationContext): VideoMode { + val node = parser.codec.readTree(parser) + + val pixelFormatValue = parsePixelFormat(node.get("pixelFormat")) + val width = node.get("width")?.asInt() ?: 640 + val height = node.get("height")?.asInt() ?: 480 + val fps = node.get("fps")?.asInt() ?: 30 + + return VideoMode(pixelFormatValue, width, height, fps) + } + + private fun parsePixelFormat(node: com.fasterxml.jackson.databind.JsonNode?): Int { + if (node == null || node.isNull) return 0 + if (node.isInt) return node.asInt() + + if (node.isTextual) { + val text = node.asText().trim() + if (text.isEmpty()) return 0 + + val enumConstant = PixelFormat.entries.firstOrNull { + it.name.equals(text, ignoreCase = true) || + it.toString().equals(text, ignoreCase = true) + } ?: return 0 + + return enumConstantToInt(enumConstant) + } + + return 0 + } + + private fun enumConstantToInt(value: PixelFormat): Int { + return try { + val method = value.javaClass.getMethod("getValue") + (method.invoke(value) as Number).toInt() + } catch (_: Exception) { + (value as Enum<*>).ordinal + } + } + } +} + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/config/WorkspaceConfigLoader.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/config/WorkspaceConfigLoader.kt index 859730d1..dca84e29 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/config/WorkspaceConfigLoader.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/config/WorkspaceConfigLoader.kt @@ -2,8 +2,8 @@ package com.github.serivesmejia.eocvsim.workspace.config import com.github.serivesmejia.eocvsim.Build import com.github.serivesmejia.eocvsim.util.SysUtil +import com.github.serivesmejia.eocvsim.util.serialization.JacksonJsonSupport import io.github.deltacv.common.util.loggerForThis -import com.google.gson.GsonBuilder import java.io.File /** @@ -13,10 +13,6 @@ import java.io.File */ class WorkspaceConfigLoader(var workspaceFile: File) { - companion object { - private val gson = GsonBuilder().setPrettyPrinting().create() - } - /** * The workspace configuration file */ @@ -34,7 +30,7 @@ class WorkspaceConfigLoader(var workspaceFile: File) { val configStr = SysUtil.loadFileStr(workspaceConfigFile) return try { - gson.fromJson(configStr, WorkspaceConfig::class.java) + JacksonJsonSupport.persistenceMapper.readValue(configStr, WorkspaceConfig::class.java) } catch(e: Exception) { logger.error("Failed to load workspace config", e) null @@ -47,7 +43,7 @@ class WorkspaceConfigLoader(var workspaceFile: File) { */ fun saveWorkspaceConfig(config: WorkspaceConfig) { config.eocvSimVersion = Build.standardVersionString - val configStr = gson.toJson(config) + val configStr = JacksonJsonSupport.persistenceMapper.writeValueAsString(config) SysUtil.saveFileStr(workspaceConfigFile, configStr) } diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt index fc85b2b2..b6f16c30 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt @@ -11,10 +11,10 @@ import io.github.deltacv.vision.internal.opmode.OpModeState import io.github.deltacv.vision.internal.opmode.RedirectToOpModeThrowableHandler import org.opencv.core.Mat import org.opencv.core.Size -import org.opencv.videoio.VideoCapture import org.openftc.easyopencv.OpenCvViewport +import org.wpilib.vision.camera.UsbCamera +import org.wpilib.vision.camera.VideoMode import java.io.File -import java.io.IOException import javax.imageio.ImageIO class VisionInputSourceProvider( @@ -25,40 +25,67 @@ class VisionInputSourceProvider( private fun isImage(path: String) = try { ImageIO.read(File(path)) != null - } catch(ex: IOException) { false } + } catch (_: Exception) { + false + } private fun isVideo(path: String): Boolean { - val capture = VideoCapture(path) + val capture = org.opencv.videoio.VideoCapture(path) val mat = Mat() capture.read(mat) - - val isVideo = !mat.empty() + val ok = !mat.empty() capture.release() + return ok + } + + private fun defaultMode(index: Int): VideoMode { + val cam = UsbCamera("$index", index) + + val mode = try { + cam.videoMode + } catch (_: Exception) { + // fallback safe mode if enumeration fails + VideoMode(0, 640, 480, 30) + } - return isVideo + cam.close() + return mode } override fun get(name: String): VisionSource { - val source = VisionInputSource(if(File(name).exists()) { - if(isImage(name)) { - ImageSource(name, Size()) - } else if(isVideo(name)) { - VideoSource(name, Size()) - } else throw IllegalArgumentException("File $name is neither an image nor a video") - } else { - val index = name.toIntOrNull() - ?: if(name == "default" || name == "Webcam 1") 0 - else null - - if(index == null) { - inputSourceManager.sources[name] ?: throw IllegalArgumentException("Input source $name not found") - } else CameraSource(index, Size(640.0, 480.0)) - }, RedirectToOpModeThrowableHandler(notifier)) + + val source = VisionInputSource( + when { + File(name).exists() -> { + when { + isImage(name) -> ImageSource(name, Size()) + isVideo(name) -> VideoSource(name, Size()) + else -> throw IllegalArgumentException( + "File $name is neither image nor video" + ) + } + } + + else -> { + val index = name.toIntOrNull() + ?: if (name == "default" || name == "Webcam 1") 0 else null + + if (index == null) { + inputSourceManager.sources[name] + ?: throw IllegalArgumentException("Input source $name not found") + } else { + // 🔥 FIX: use real VideoMode instead of Size hack + CameraSource(index, defaultMode(index)) + } + } + }, + RedirectToOpModeThrowableHandler(notifier) + ) notifier.onStateChange { - when(notifier.state) { + when (notifier.state) { OpModeState.STOPPED -> { source.stop() removeListener() @@ -71,5 +98,4 @@ class VisionInputSourceProvider( } override fun viewport() = viewport - } \ 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 e4afb42a..523f6104 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 @@ -25,9 +25,9 @@ package io.github.deltacv.eocvsim.plugin.security.superaccess import com.github.serivesmejia.eocvsim.util.extension.fileHash import com.github.serivesmejia.eocvsim.util.extension.plus +import com.github.serivesmejia.eocvsim.util.serialization.JacksonJsonSupport import io.github.deltacv.common.util.loggerForThis -import com.github.serivesmejia.eocvsim.util.serialization.PolymorphicAdapter -import com.google.gson.GsonBuilder +import com.fasterxml.jackson.annotation.JsonTypeInfo import com.moandjiezana.toml.Toml import io.github.deltacv.eocvsim.gui.dialog.SuperAccessRequest import io.github.deltacv.eocvsim.plugin.loader.PluginInfo @@ -51,15 +51,13 @@ import kotlin.system.exitProcess object SuperAccessDaemon { val logger by loggerForThis() - val gson = GsonBuilder() - .registerTypeHierarchyAdapter(SuperAccessMessage::class.java, PolymorphicAdapter("message")) - .registerTypeHierarchyAdapter(SuperAccessResponse::class.java, PolymorphicAdapter("response")) - .create() + val mapper = JacksonJsonSupport.ipcMapper @get:Synchronized @set:Synchronized private var uniqueId = 0 + @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class") sealed class SuperAccessMessage { data class Request(var pluginPath: String, var signature: MutablePluginSignature, var reason: String) : SuperAccessMessage() data class Check(var pluginPath: String) : SuperAccessMessage() @@ -67,6 +65,7 @@ object SuperAccessDaemon { var id = uniqueId++ } + @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class") sealed class SuperAccessResponse(val id: Int) { class Success(id: Int) : SuperAccessResponse(id) class Failure(id: Int) : SuperAccessResponse(id) @@ -105,7 +104,7 @@ object SuperAccessDaemon { } override fun onMessage(msg: String) { - val message = gson.fromJson(msg, SuperAccessMessage::class.java) + val message = mapper.readValue(msg, SuperAccessMessage::class.java) executor.submit { when (message) { @@ -125,7 +124,7 @@ object SuperAccessDaemon { val parser = parsePlugin(pluginFile) ?: run { logger.error("Failed to parse plugin at ${message.pluginPath}") - (gson.toJson(SuperAccessResponse.Failure(message.id))) + send(mapper.writeValueAsString(SuperAccessResponse.Failure(message.id))) return@handleRequest } @@ -225,12 +224,12 @@ object SuperAccessDaemon { private fun accessGranted(id: Int, pluginPath: String) { access[pluginPath] = true - send(gson.toJson(SuperAccessResponse.Success(id))) + send(mapper.writeValueAsString(SuperAccessResponse.Success(id))) } private fun accessDenied(id: Int, pluginPath: String) { access[pluginPath] = false - send(gson.toJson(SuperAccessResponse.Failure(id))) + send(mapper.writeValueAsString(SuperAccessResponse.Failure(id))) } override fun onClose(p0: Int, p1: String?, p2: Boolean) { 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 f2c64eec..178948a6 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,6 +24,7 @@ package io.github.deltacv.eocvsim.plugin.security.superaccess import com.github.serivesmejia.eocvsim.util.JavaProcess +import com.github.serivesmejia.eocvsim.util.serialization.JacksonJsonSupport import io.github.deltacv.common.util.loggerForThis import org.java_websocket.WebSocket import org.java_websocket.handshake.ClientHandshake @@ -77,7 +78,7 @@ class SuperAccessDaemonClient( return } - server.broadcast(SuperAccessDaemon.gson.toJson(request)) + server.broadcast(JacksonJsonSupport.ipcMapper.writeValueAsString(request)) server.addResponseReceiver(request.id) { response -> val result = when (response) { @@ -114,7 +115,7 @@ class SuperAccessDaemonClient( val condition = lock.newCondition() val check = SuperAccessDaemon.SuperAccessMessage.Check(file.absolutePath) - server.broadcast(SuperAccessDaemon.gson.toJson(check)) + server.broadcast(JacksonJsonSupport.ipcMapper.writeValueAsString(check)) var hasAccess = false @@ -197,7 +198,7 @@ class SuperAccessDaemonClient( } override fun onMessage(ws: WebSocket, msg: String) { - val response = SuperAccessDaemon.gson.fromJson(msg, SuperAccessDaemon.SuperAccessResponse::class.java) + val response = JacksonJsonSupport.ipcMapper.readValue(msg, SuperAccessDaemon.SuperAccessResponse::class.java) synchronized(responseReceiverLock) { for ((condition, receiver) in responseReceiver.toMap()) { From 24a2532309b8b5e5a6d89cbb3391ed46042b1ca4 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 12 May 2026 04:50:03 -0600 Subject: [PATCH 17/40] Fix deserialization issues, add timeout settings in the gui for webcams --- .../java/android/annotation/SystemApi.java | 2 +- .../serivesmejia/eocvsim/config/Config.java | 15 +- .../eocvsim/gui/dialog/Configuration.kt | 37 +++-- .../gui/dialog/source/CreateCameraSource.kt | 136 +++++++++++++++--- .../eocvsim/input/source/CameraSource.kt | 119 +++++++++++---- .../eocvsim/pipeline/PipelineManager.kt | 4 +- .../plugin/api/impl/InputSourceApisImpl.kt | 12 +- .../util/serialization/JacksonJsonSupport.kt | 14 +- .../eocvsim/workspace/WorkspaceManager.kt | 4 +- 9 files changed, 248 insertions(+), 95 deletions(-) diff --git a/Common/src/main/java/android/annotation/SystemApi.java b/Common/src/main/java/android/annotation/SystemApi.java index 58d4dd9c..ce3d9bd4 100644 --- a/Common/src/main/java/android/annotation/SystemApi.java +++ b/Common/src/main/java/android/annotation/SystemApi.java @@ -37,7 +37,7 @@ */ @Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE}) @Retention(RetentionPolicy.RUNTIME) -@Repeatable(SystemApi.Container.class) // TODO(b/146727827): make this non-repeatable +@Repeatable(SystemApi.Container.class) public @interface SystemApi { enum Client { /** 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 46a72799..5b41416b 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 @@ -26,32 +26,28 @@ import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel; import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanelConfig; import com.github.serivesmejia.eocvsim.gui.theme.Theme; -import com.github.serivesmejia.eocvsim.gui.util.WebcamDriver; import com.github.serivesmejia.eocvsim.pipeline.PipelineFps; import com.github.serivesmejia.eocvsim.pipeline.PipelineTimeout; import com.github.serivesmejia.eocvsim.pipeline.compiled.CompiledPipelineManager; import org.opencv.core.Size; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; public class Config { public volatile Theme simTheme = Theme.Dark; - @Deprecated - public volatile double zoom = 1; - public volatile PipelineFps pipelineMaxFps = PipelineFps.MEDIUM; public volatile PipelineTimeout pipelineTimeout = PipelineTimeout.MEDIUM; public volatile boolean pauseOnImages = true; + public volatile double webcamOpenTimeoutSec = 5.0; + public volatile double webcamNewFrameTimeoutSec = 3.0; + public volatile Size videoRecordingSize = new Size(640, 480); public volatile PipelineFps videoRecordingFps = PipelineFps.MEDIUM; - public volatile WebcamDriver preferredWebcamDriver = WebcamDriver.OpenPnp; - public volatile String workspacePath = CompiledPipelineManager.Companion.getDEF_WORKSPACE_FOLDER().getAbsolutePath(); + public volatile String workspacePath = CompiledPipelineManager.Companion.getDEF_WORKSPACE_FOLDER().getAbsolutePath(); public volatile TunableFieldPanelConfig.Config globalTunableFieldsConfig = new TunableFieldPanelConfig.Config( @@ -63,9 +59,6 @@ public class Config { public volatile HashMap specificTunableFieldConfig = new HashMap<>(); - @Deprecated - public volatile List superAccessPluginHashes = new ArrayList<>(); - public volatile boolean autoAcceptSuperAccessOnTrusted = true; public volatile HashMap flags = new HashMap<>(); diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.kt index b7fc1de0..ce90e8f3 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.kt @@ -23,30 +23,26 @@ package com.github.serivesmejia.eocvsim.gui.dialog -import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.LifecycleSignal -import com.github.serivesmejia.eocvsim.config.Config import com.github.serivesmejia.eocvsim.config.ConfigManager import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.gui.component.input.EnumComboBox import com.github.serivesmejia.eocvsim.gui.component.input.SizeFields import com.github.serivesmejia.eocvsim.gui.theme.Theme -import com.github.serivesmejia.eocvsim.gui.util.WebcamDriver import com.github.serivesmejia.eocvsim.pipeline.PipelineFps import com.github.serivesmejia.eocvsim.pipeline.PipelineTimeout import com.github.serivesmejia.eocvsim.util.event.EventHandler import kotlinx.coroutines.channels.Channel +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named import java.awt.FlowLayout import java.awt.GridBagConstraints import java.awt.GridBagLayout import java.awt.GridLayout import javax.swing.* -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject -import org.koin.core.qualifier.named - class Configuration : KoinComponent { private val visualizer: Visualizer by inject() @@ -63,9 +59,10 @@ class Configuration : KoinComponent { private val superAccessCheckBox: JCheckBox private val prefersPaperVisionCheckbox: JCheckBox private val pauseOnImageCheckBox: JCheckBox + private val webcamOpenTimeoutSpinner: JSpinner + private val webcamNewFrameTimeoutSpinner: JSpinner private val pipelineTimeoutComboBox: EnumComboBox private val pipelineFpsComboBox: EnumComboBox - private val preferredWebcamDriver: EnumComboBox private val videoRecordingSize: SizeFields private val videoRecordingFpsComboBox: EnumComboBox private val acceptButton: JButton @@ -95,17 +92,18 @@ class Configuration : KoinComponent { pauseOnImageCheckBox = JCheckBox("Pause with Image Sources").apply { isSelected = config.pauseOnImages } - preferredWebcamDriver = EnumComboBox( - "Preferred Webcam Driver: ", - WebcamDriver::class.java, - WebcamDriver.entries.toTypedArray() - ).apply { - removeEnumOption(WebcamDriver.OpenIMAJ) - selectedEnum = config.preferredWebcamDriver - } - val inputSourcesPanel = JPanel(GridLayout(2, 1, 1, 8)).apply { + webcamOpenTimeoutSpinner = JSpinner(SpinnerNumberModel(config.webcamOpenTimeoutSec, 0.1, 60.0, 0.1)) + webcamNewFrameTimeoutSpinner = JSpinner(SpinnerNumberModel(config.webcamNewFrameTimeoutSec, 0.1, 60.0, 0.1)) + val inputSourcesPanel = JPanel(GridLayout(3, 1, 1, 8)).apply { add(JPanel(FlowLayout()).apply { add(pauseOnImageCheckBox) }) - add(preferredWebcamDriver) + add(JPanel(FlowLayout()).apply { + add(JLabel("Timeout to start camera stream (seconds): ")) + add(webcamOpenTimeoutSpinner) + }) + add(JPanel(FlowLayout()).apply { + add(JLabel("Timeout for new camera frame (seconds): ")) + add(webcamNewFrameTimeoutSpinner) + }) } // --- Processing Tab --- @@ -189,7 +187,8 @@ class Configuration : KoinComponent { config.simTheme = userSelectedTheme config.pauseOnImages = pauseOnImageCheckBox.isSelected - config.preferredWebcamDriver = preferredWebcamDriver.selectedEnum + config.webcamOpenTimeoutSec = (webcamOpenTimeoutSpinner.value as Number).toDouble() + config.webcamNewFrameTimeoutSec = (webcamNewFrameTimeoutSpinner.value as Number).toDouble() config.pipelineTimeout = pipelineTimeoutComboBox.selectedEnum config.pipelineMaxFps = pipelineFpsComboBox.selectedEnum config.videoRecordingSize = videoRecordingSize.currentSize diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt index 4d07fc90..2eec0da9 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt @@ -21,7 +21,9 @@ class CreateCameraSource : KoinComponent { private val camerasComboBox = JComboBox() private val modesComboBox = JComboBox() - private val nameTextField = JTextField(15) + private val nameTextField = JTextField(20) + private val sameCameraCheckBox = JCheckBox("Match to exact port") + private val createButton = JButton("Create") private val dialog = JDialog(visualizer.frame) @@ -30,7 +32,6 @@ class CreateCameraSource : KoinComponent { private var modes: Array = emptyArray() init { - cameraInfos = try { UsbCamera.enumerateUsbCameras() } catch (t: Throwable) { @@ -44,26 +45,38 @@ class CreateCameraSource : KoinComponent { } val root = JPanel(GridBagLayout()) + val gbc = GridBagConstraints().apply { fill = GridBagConstraints.HORIZONTAL - insets = Insets(5, 5, 5, 5) + weightx = 1.0 + insets = Insets(6, 6, 6, 6) } + // ---------------- CAMERA LIST ---------------- + if (cameraInfos.isEmpty()) { camerasComboBox.addItem("No Cameras Found") + createButton.isEnabled = false } else { cameraInfos.forEach { camerasComboBox.addItem(it.name) } + camerasComboBox.selectedIndex = 0 + + updateAutomaticName() loadModes(0) } camerasComboBox.addActionListener { + updateAutomaticName() loadModes(camerasComboBox.selectedIndex) } + // ---------------- MODE RENDERER ---------------- + modesComboBox.renderer = object : DefaultListCellRenderer() { + override fun getListCellRendererComponent( list: JList<*>, value: Any?, @@ -71,12 +84,23 @@ class CreateCameraSource : KoinComponent { isSelected: Boolean, cellHasFocus: Boolean ): Component { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus) + + super.getListCellRendererComponent( + list, + value, + index, + isSelected, + cellHasFocus + ) val mode = value as? VideoMode text = if (mode != null) { - "${mode.width}x${mode.height} @ ${mode.fps}fps (${mode.pixelFormat})" + buildString { + append("${mode.width}x${mode.height}") + append(" @ ${mode.fps}fps") + append(" (${mode.pixelFormat})") + } } else { "Unknown" } @@ -85,58 +109,116 @@ class CreateCameraSource : KoinComponent { } } - gbc.gridx = 0; gbc.gridy = 0 + // ---------------- FORM ---------------- + + gbc.gridx = 0 + gbc.gridy = 0 + gbc.weightx = 0.0 root.add(JLabel("Camera:"), gbc) + gbc.gridx = 1 + gbc.weightx = 1.0 root.add(camerasComboBox, gbc) - gbc.gridx = 0; gbc.gridy = 1 + gbc.gridx = 0 + gbc.gridy = 1 + gbc.weightx = 0.0 root.add(JLabel("Mode:"), gbc) + gbc.gridx = 1 + gbc.weightx = 1.0 root.add(modesComboBox, gbc) - gbc.gridx = 0; gbc.gridy = 2 + gbc.gridx = 0 + gbc.gridy = 2 + gbc.weightx = 0.0 root.add(JLabel("Name:"), gbc) + gbc.gridx = 1 - nameTextField.text = "CameraSource-${inputSourceManager.sources.size + 1}" + gbc.weightx = 1.0 root.add(nameTextField, gbc) - val buttons = JPanel() + gbc.gridx = 1 + gbc.gridy = 3 + gbc.weightx = 1.0 + root.add(sameCameraCheckBox, gbc) + + // ---------------- BUTTONS ---------------- + + val buttons = JPanel(FlowLayout(FlowLayout.RIGHT)) + + val cancelButton = JButton("Cancel") + buttons.add(createButton) - val cancel = JButton("Cancel") - buttons.add(cancel) + buttons.add(cancelButton) - gbc.gridx = 0; gbc.gridy = 3 + gbc.gridx = 0 + gbc.gridy = 4 gbc.gridwidth = 2 + root.add(buttons, gbc) - createButton.addActionListener { create() } - cancel.addActionListener { close() } + // ---------------- EVENTS ---------------- + + createButton.addActionListener { + create() + } + + cancelButton.addActionListener { + close() + } + + // ---------------- DIALOG ---------------- dialog.contentPane.add(root) + dialog.pack() + dialog.minimumSize = dialog.size + dialog.setLocationRelativeTo(null) dialog.isVisible = true } + private fun updateAutomaticName() { + val info = cameraInfos.getOrNull(camerasComboBox.selectedIndex) + ?: return + + nameTextField.text = inputSourceManager.tryName(info.name) + } + private fun loadModes(index: Int) { + val info = cameraInfos.getOrNull(index) ?: return try { + val cam = UsbCamera(info.name, info.dev) modes = cam.enumerateVideoModes() + .distinctBy { + "${it.width}x${it.height}_${it.fps}_${it.pixelFormat}" + } + .sortedWith( + compareByDescending { + it.width * it.height + }.thenByDescending { + it.fps + } + ) + .toTypedArray() cam.close() + } catch (t: Throwable) { + t.printStackTrace() modes = emptyArray() } modesComboBox.removeAllItems() - for (m in modes) { - modesComboBox.addItem(m) + for (mode in modes) { + modesComboBox.addItem(mode) } if (modes.isNotEmpty()) { @@ -145,15 +227,27 @@ class CreateCameraSource : KoinComponent { } private fun create() { + val camIndex = camerasComboBox.selectedIndex - val info = cameraInfos.getOrNull(camIndex) ?: return - val mode = modes.getOrNull(modesComboBox.selectedIndex) ?: return + val info = cameraInfos.getOrNull(camIndex) + ?: return + + val mode = modes.getOrNull(modesComboBox.selectedIndex) + ?: return onMainUpdate.once { + inputSourceManager.addInputSource( - nameTextField.text, - CameraSource(info.name, mode), + nameTextField.text.trim(), + CameraSource( + info.name, + info.dev, + sameCameraCheckBox.isSelected, + info.vendorId, + info.productId, + mode + ), true ) } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt index 34144d11..8444c5f5 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt @@ -4,12 +4,13 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect import com.fasterxml.jackson.annotation.JsonProperty import com.github.serivesmejia.eocvsim.input.InputSource import com.github.serivesmejia.eocvsim.input.InputSourceInitializer +import com.github.serivesmejia.eocvsim.config.ConfigManager import io.github.deltacv.common.util.loggerForThis import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import org.opencv.core.Mat import org.opencv.core.Size import org.opencv.imgproc.Imgproc -import org.openftc.easyopencv.MatRecycler import org.wpilib.util.PixelFormat import org.wpilib.vision.camera.CvSink import org.wpilib.vision.camera.UsbCamera @@ -31,8 +32,11 @@ class CameraSource : InputSource, KoinComponent { override val hasSlowInitialization: Boolean get() = true - @Transient var webcamIndex: Int = 0 - @JsonProperty @JvmField var webcamName: String = "" + @JsonProperty @JvmField var cameraPortIndex: Int = -1 + @JsonProperty @JvmField var exactPortMatch: Boolean = false + @JsonProperty @JvmField var vendorId: Int? = null + @JsonProperty @JvmField var productId: Int? = null + @Transient var webcamName: String = "" @JsonProperty @JvmField var videoMode: VideoMode? = null @@ -47,8 +51,8 @@ class CameraSource : InputSource, KoinComponent { @Transient var isLegacyByIndex = false - @Transient private var matRecycler = MatRecycler(3) @Transient private var capTimeNanos: Long = 0 + private val configManager: ConfigManager by inject() private val logger by loggerForThis() constructor() : super() @@ -58,8 +62,31 @@ class CameraSource : InputSource, KoinComponent { this.videoMode = videoMode } - constructor(webcamIndex: Int, videoMode: VideoMode?) : super() { - this.webcamIndex = webcamIndex + constructor(webcamName: String?, vendorId: Int?, productId: Int?, videoMode: VideoMode?) : super() { + this.webcamName = webcamName ?: "" + this.vendorId = vendorId + this.productId = productId + this.videoMode = videoMode + } + + constructor( + webcamName: String?, + cameraPortIndex: Int, + exactPortMatch: Boolean, + vendorId: Int?, + productId: Int?, + videoMode: VideoMode? + ) : super() { + this.webcamName = webcamName ?: "" + this.cameraPortIndex = cameraPortIndex + this.exactPortMatch = exactPortMatch + this.vendorId = vendorId + this.productId = productId + this.videoMode = videoMode + } + + constructor(cameraPortIndex: Int, videoMode: VideoMode?) : super() { + this.cameraPortIndex = cameraPortIndex this.videoMode = videoMode this.isLegacyByIndex = true } @@ -75,17 +102,57 @@ class CameraSource : InputSource, KoinComponent { if (initialized) return false initialized = true - val cam = if (webcamName.isNotEmpty()) { - val infos = UsbCamera.enumerateUsbCameras() - val info = infos.firstOrNull { it.name == webcamName } - ?: run { - logger.error("Camera not found: $webcamName") + val matchedInfo = when { + exactPortMatch -> { + val infos = UsbCamera.enumerateUsbCameras() + infos.firstOrNull { + cameraPortIndex >= 0 && + it.dev == cameraPortIndex && + vendorId != null && productId != null && + it.vendorId == vendorId && + it.productId == productId + } ?: run { + logger.error("Camera not found on the same connection: $cameraPortIndex") + return false + } + } + + cameraPortIndex >= 0 -> { + val infos = UsbCamera.enumerateUsbCameras() + infos.firstOrNull { it.dev == cameraPortIndex } + ?: run { + logger.error("Camera not found on port: $cameraPortIndex") + return false + } + } + + vendorId != null && productId != null -> { + val infos = UsbCamera.enumerateUsbCameras() + infos.firstOrNull { + it.vendorId == vendorId && it.productId == productId + } ?: run { + logger.error("Camera not found by VID/PID: ${vendorId}:${productId}") return false } + } + + webcamName.isNotEmpty() -> { + val infos = UsbCamera.enumerateUsbCameras() + infos.firstOrNull { it.name == webcamName } + ?: run { + logger.error("Camera not found: $webcamName") + return false + } + } + + else -> null + } - UsbCamera(webcamName, info.dev) + val cam = if (matchedInfo != null) { + webcamName = matchedInfo.name + UsbCamera(matchedInfo.name, matchedInfo.dev) } else { - UsbCamera("$webcamIndex", webcamIndex) + UsbCamera("$cameraPortIndex", cameraPortIndex) } camera = cam @@ -102,25 +169,21 @@ class CameraSource : InputSource, KoinComponent { val mode = cam.videoMode logger.info( - "Camera started: $webcamName ${mode?.stringify()}" + "Camera started: ${matchedInfo?.name ?: webcamName.ifEmpty { "Camera $cameraPortIndex" }} ${mode?.stringify()}" ) - cvSink = CvSink("eocvsim_sink_$webcamIndex", PixelFormat.BGR).also { + cvSink = CvSink("eocvsim_sink_$cameraPortIndex", PixelFormat.BGR).also { it.source = cam } - val test = matRecycler.takeMatOrNull() ?: return false - val ok = cvSink!!.grabFrame(test, 5.0) + val ok = cvSink!!.grabFrame(lastFrame, configManager.config.webcamOpenTimeoutSec) - if (ok == 0L || test.empty()) { - test.returnMat() + if (ok == 0L || lastFrame.empty()) { logger.error("Failed to open camera: ${cvSink!!.error}") return false } - test.returnMat() - - currentWebcamIndex = webcamIndex + currentWebcamIndex = cameraPortIndex return true } @@ -147,7 +210,7 @@ class CameraSource : InputSource, KoinComponent { override fun update(): Mat { if (isPaused) return lastFrame - val grabTime = cvSink?.grabFrame(lastFrame, 3.0) ?: 0L + val grabTime = cvSink?.grabFrame(lastFrame, configManager.config.webcamNewFrameTimeoutSec) ?: 0L if(lastFrame.empty()) { return lastFrame @@ -159,7 +222,7 @@ class CameraSource : InputSource, KoinComponent { } override fun onPause() { - cvSink?.grabFrame(lastFrame, 2.0) + cvSink?.grabFrame(lastFrame, configManager.config.webcamNewFrameTimeoutSec) cvSink?.close() camera?.close() currentWebcamIndex = -1 @@ -171,16 +234,16 @@ class CameraSource : InputSource, KoinComponent { override fun internalCloneSource(): InputSource = if (isLegacyByIndex) { - CameraSource(webcamIndex, videoMode) + CameraSource(cameraPortIndex, videoMode) } else { - CameraSource(webcamName, videoMode) + CameraSource(webcamName, cameraPortIndex, exactPortMatch, vendorId, productId, videoMode) } override val fileFilters: FileFilter? get() = null override val captureTimeNanos: Long get() = capTimeNanos override fun toString() = - "CameraSource($webcamName, $webcamIndex, ${videoMode?.stringify()})" + "CameraSource($webcamName, port=$cameraPortIndex, exactPortMatch=$exactPortMatch, vid=$vendorId, pid=$productId, ${videoMode?.stringify()})" - private fun VideoMode.stringify() = "${width}x${height}@${fps} ${pixelFormat}" + private fun VideoMode.stringify() = "${width}x${height}@${fps} $pixelFormat" } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt index 74d38405..e55bb092 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt @@ -455,9 +455,7 @@ class PipelineManager : PhaseOrchestrableBase(), KoinComponent { } } - override suspend fun destroy() { - // TODO: check if we need to do anything here - } + override suspend fun destroy() { } private fun updateExceptionTracker(ex: Throwable? = null) { pipelineExceptionTracker.update( diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt index 419cd8ed..0053677a 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt @@ -31,13 +31,19 @@ import com.github.serivesmejia.eocvsim.input.source.VideoSource import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin import io.github.deltacv.eocvsim.plugin.api.InputSourceApi import io.github.deltacv.eocvsim.plugin.api.InputSourceManagerApi +import org.opencv.core.Size class InputSourceApiImpl(owner: EOCVSimPlugin, val internalInputSource: com.github.serivesmejia.eocvsim.input.InputSource) : InputSourceApi(owner) { - override val isPaused by liveApiField { internalInputSource.isPaused } + override val isPaused by liveApiField { internalInputSource.isPaused } override val data by apiField { when(internalInputSource) { - is CameraSource -> TODO() // New.Camera(internalInputSource.webcamName, internalInputSource.size) + is CameraSource -> New.Camera( + internalInputSource.camera?.name ?: "", + Size(internalInputSource.videoMode?.width?.toDouble() ?: 0.0, + internalInputSource.videoMode?.height?.toDouble() ?: 0.0 + ) + ) is ImageSource -> New.Image(internalInputSource.imgPath, internalInputSource.size) is VideoSource -> New.Video(internalInputSource.videoPath, internalInputSource.size) is HttpSource -> New.Http(internalInputSource.url) @@ -46,7 +52,7 @@ class InputSourceApiImpl(owner: EOCVSimPlugin, val internalInputSource: com.gith } override val name: String by apiField { internalInputSource.name } - override val creationTime by apiField { internalInputSource.creationTime } + override val creationTime by apiField { internalInputSource.creationTime } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/JacksonJsonSupport.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/JacksonJsonSupport.kt index d1c7ea11..60417efe 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/JacksonJsonSupport.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/JacksonJsonSupport.kt @@ -40,12 +40,7 @@ import com.fasterxml.jackson.module.kotlin.registerKotlinModule object JacksonJsonSupport { @JvmField - val persistenceMapper: ObjectMapper = createMapper() - - @JvmField - val ipcMapper: ObjectMapper = createMapper() - - private fun createMapper(): ObjectMapper = ObjectMapper() + val persistenceMapper: ObjectMapper = ObjectMapper() .registerKotlinModule() .registerModule(VideoModeJackson.module()) .setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE) @@ -53,4 +48,11 @@ object JacksonJsonSupport { .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .enable(SerializationFeature.INDENT_OUTPUT) + @JvmField + val ipcMapper: ObjectMapper = ObjectMapper() + .registerKotlinModule() + .setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE) + .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .enable(SerializationFeature.INDENT_OUTPUT) } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt index f78354e4..a95ef257 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt @@ -236,9 +236,7 @@ class WorkspaceManager : PhaseOrchestrableBase(), KoinComponent { CompiledPipelineManager.DEF_WORKSPACE_FOLDER } - override suspend fun run() { - // TODO: check if we need to do anything here - } + override suspend fun run() { } override suspend fun destroy() { stopFileWatcher() From 4a076b7de01f362289d4c7496f5c562cf915c32a Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 12 May 2026 16:58:53 -0600 Subject: [PATCH 18/40] Shorten license headers & update github workflows to java 25 --- .github/workflows/build_ci.yml | 6 +- .github/workflows/release_ci.yml | 2 +- .../java/android/annotation/AnyThread.java | 7 +- .../java/android/annotation/ColorInt.java | 7 +- .../java/android/annotation/ColorLong.java | 7 +- .../java/android/annotation/HalfFloat.java | 7 +- .../main/java/android/annotation/IntDef.java | 7 +- .../java/android/annotation/IntRange.java | 7 +- .../main/java/android/annotation/NonNull.java | 7 +- .../java/android/annotation/Nullable.java | 7 +- .../main/java/android/annotation/Size.java | 7 +- .../android/annotation/SuppressAutoDoc.java | 7 +- .../java/android/annotation/SuppressLint.java | 7 +- .../java/android/annotation/SystemApi.java | 7 +- .../main/java/android/graphics/Bitmap.java | 21 +- .../main/java/android/graphics/Canvas.java | 22 +- .../src/main/java/android/graphics/Color.java | 7 +- .../java/android/graphics/ColorSpace.java | 7 +- .../main/java/android/graphics/FontCache.java | 21 +- .../src/main/java/android/graphics/Paint.java | 27 +-- .../src/main/java/android/graphics/Path.java | 27 +-- .../src/main/java/android/graphics/Rect.java | 7 +- .../android/graphics/TemporaryBuffer.java | 7 +- .../main/java/android/graphics/Typeface.java | 21 +- .../main/java/android/hardware/Camera.java | 6 + .../main/java/android/hardware/DataSpace.java | 7 +- .../src/main/java/android/opengl/Matrix.java | 7 +- .../src/main/java/android/util/ArraySet.java | 7 +- .../java/android/util/ContainerHelpers.java | 7 +- Common/src/main/java/android/util/Half.java | 7 +- .../java/android/util/MapCollections.java | 7 +- Common/src/main/java/android/util/Size.java | 7 +- .../java/android/util/SparseIntArray.java | 7 +- .../java/androidx/annotation/ColorInt.java | 7 +- .../java/androidx/annotation/NonNull.java | 7 +- .../java/androidx/annotation/Nullable.java | 7 +- .../java/androidx/annotation/StringRes.java | 7 +- .../com/android/internal/util/ArrayUtils.java | 7 +- .../internal/util/GrowingArrayUtils.java | 7 +- .../com/formdev/flatlaf/demo/HintManager.java | 7 +- .../gui/component/CollapsiblePanelX.kt | 23 +- .../eocvsim/gui/component/PopupX.kt | 30 +-- .../eocvsim/gui/component/SliderX.kt | 30 +-- .../serivesmejia/eocvsim/gui/util/Enums.kt | 7 +- .../gui/util/ValidCharactersDocumentFilter.kt | 30 +-- .../eocvsim/gui/util/extension/SwingExt.kt | 30 +-- .../DefaultPipelineInstantiator.kt | 22 +- .../instantiator/PipelineInstantiator.kt | 22 +- .../plugin/output/PluginOutputHandler.kt | 30 +-- .../eocvsim/util/JavaProcess.java | 36 +-- .../eocvsim/util/ReflectUtil.java | 29 +-- .../serivesmejia/eocvsim/util/StrUtil.java | 29 +-- .../serivesmejia/eocvsim/util/SysUtil.java | 29 +-- .../eocvsim/util/event/EventHandler.kt | 17 +- .../eocvsim/util/event/EventListener.kt | 22 +- .../eocvsim/util/extension/FileExt.kt | 30 +-- .../eocvsim/util/extension/NumberExt.kt | 30 +-- .../eocvsim/util/extension/StrExt.kt | 7 +- .../eocvsim/util/fps/FpsCounter.kt | 30 +-- .../eocvsim/util/fps/FpsLimiter.kt | 30 +-- .../eocvsim/util/io/EOCVSimFolder.kt | 7 +- .../serivesmejia/eocvsim/util/io/Lock.kt | 7 +- .../util/orchestration/Orchestrable.kt | 7 +- .../util/orchestration/Orchestrator.kt | 6 + .../eventloop/opmode/Autonomous.java | 7 +- .../robotcore/eventloop/opmode/Disabled.java | 7 +- .../robotcore/eventloop/opmode/TeleOp.java | 7 +- .../exception/RobotCoreException.java | 7 +- .../qualcomm/robotcore/util/ElapsedTime.java | 7 +- .../robotcore/util/MovingStatistics.java | 7 +- .../com/qualcomm/robotcore/util/Range.java | 7 +- .../com/qualcomm/robotcore/util/RobotLog.java | 6 + .../qualcomm/robotcore/util/SerialNumber.java | 6 + .../qualcomm/robotcore/util/SortOrder.java | 21 +- .../qualcomm/robotcore/util/Statistics.java | 7 +- .../main/java/dalvik/system/VMRuntime.java | 6 + .../common/image/BufferedImageRecycler.java | 21 +- .../image/DynamicBufferedImageRecycler.java | 22 +- .../deltacv/common/image/MatPoster.java | 6 + .../util/PipelineStatisticsCalculator.kt | 7 +- .../deltacv/common/util/LoggerDelegates.kt | 7 +- .../deltacv/common/util/ParsedVersion.kt | 7 +- .../deltacv/eocvsim/plugin/EOCVSimPlugin.kt | 6 + .../github/deltacv/eocvsim/plugin/Folders.kt | 30 +-- .../github/deltacv/eocvsim/plugin/api/Api.kt | 30 +-- .../deltacv/eocvsim/plugin/api/ApiDisabler.kt | 30 +-- .../deltacv/eocvsim/plugin/api/ConfigApi.kt | 30 +-- .../eocvsim/plugin/api/DialogFactoryApi.kt | 30 +-- .../deltacv/eocvsim/plugin/api/EOCVSimApi.kt | 29 +-- .../deltacv/eocvsim/plugin/api/HookApi.kt | 29 +-- .../eocvsim/plugin/api/InputSourceApis.kt | 30 +-- .../eocvsim/plugin/api/PipelineManagerApi.kt | 30 +-- .../deltacv/eocvsim/plugin/api/SwingApis.kt | 30 +-- .../eocvsim/plugin/api/VariableTunerApi.kt | 30 +-- .../eocvsim/plugin/api/VisualizerApi.kt | 30 +-- .../plugin/api/VisualizerComponentsApi.kt | 7 +- .../api/exception/EOCVSimApiException.kt | 30 +-- .../eocvsim/plugin/loader/Exceptions.kt | 22 +- .../eocvsim/plugin/loader/FilePluginLoader.kt | 30 +-- .../eocvsim/plugin/loader/PluginContext.kt | 29 +-- .../eocvsim/plugin/loader/PluginLoader.kt | 29 +-- .../eocvsim/plugin/loader/Providers.kt | 30 +-- .../eocvsim/plugin/security/Authority.kt | 38 +--- .../plugin/security/KeyGeneratorTool.kt | 30 +-- .../security/PluginSignatureVerifier.kt | 30 +-- .../plugin/security/PluginSigningTool.kt | 30 +-- .../eocvsim/sandbox/nio/SandboxFileSystem.kt | 22 +- .../restrictions/DynamicCodeRestrictions.kt | 19 +- .../eocvsim/virtualreflect/VirtualField.kt | 22 +- .../virtualreflect/VirtualReflectContext.kt | 22 +- .../virtualreflect/VirtualReflection.kt | 22 +- .../virtualreflect/jvm/JvmVirtualField.kt | 22 +- .../jvm/JvmVirtualReflectContext.kt | 22 +- .../jvm/JvmVirtualReflection.kt | 22 +- .../eocvsim/virtualreflect/jvm/Label.java | 21 +- .../main/java/libcore/util/EmptyArray.java | 7 +- Common/src/main/java/libcore/util/FP16.java | 7 +- .../ftc/robotcore/external/Const.java | 6 + .../ftc/robotcore/external/Func.java | 6 + .../ftc/robotcore/external/NonConst.java | 6 + .../ftc/robotcore/external/Predicate.java | 7 +- .../ftc/robotcore/external/Telemetry.java | 6 + .../robotcore/external/android/util/Size.java | 7 +- .../robotcore/external/function/Consumer.java | 7 +- .../external/matrices/ColumnMajorMatrixF.java | 6 + .../external/matrices/ColumnMatrixF.java | 6 + .../external/matrices/DenseMatrixF.java | 6 + .../external/matrices/GeneralMatrixF.java | 6 + .../robotcore/external/matrices/MatrixF.java | 7 +- .../external/matrices/OpenGLMatrix.java | 6 + .../external/matrices/RowMajorMatrixF.java | 6 + .../external/matrices/RowMatrixF.java | 6 + .../external/matrices/SliceMatrixF.java | 6 + .../robotcore/external/matrices/VectorF.java | 6 + .../external/navigation/Acceleration.java | 6 + .../external/navigation/AngleUnit.java | 6 + .../external/navigation/AxesOrder.java | 7 +- .../external/navigation/AxesReference.java | 6 + .../robotcore/external/navigation/Axis.java | 6 + .../external/navigation/DistanceUnit.java | 6 + .../external/navigation/Orientation.java | 7 +- .../robotcore/external/navigation/Pose3D.java | 6 + .../external/navigation/Position.java | 6 + .../external/navigation/Quaternion.java | 6 + .../navigation/UnnormalizedAngleUnit.java | 6 + .../external/navigation/Velocity.java | 6 + .../navigation/YawPitchRollAngles.java | 6 + .../camera/calibration/CameraCalibration.java | 6 + .../CameraCalibrationIdentity.java | 7 +- .../camera/calibration/CameraIntrinsics.java | 7 +- .../VendorProductCalibrationIdentity.java | 6 + .../collections/EvictingBlockingQueue.java | 7 +- .../collections/MutableReference.java | 6 + .../robotcore/internal/system/AppUtil.java | 6 + .../ftc/robotcore/internal/system/Assert.java | 7 +- .../ftc/robotcore/internal/system/Misc.java | 7 +- .../robotcore/robocol/TelemetryMessage.java | 6 + .../org/openftc/easyopencv/MatRecycler.java | 27 +-- .../openftc/easyopencv/OpenCvPipeline.java | 22 +- EOCV-Sim/build.gradle | 1 + .../github/serivesmejia/eocvsim/EOCVSim.kt | 72 +++--- .../github/serivesmejia/eocvsim/Lifecycle.kt | 7 +- .../com/github/serivesmejia/eocvsim/Main.kt | 41 +--- .../com/github/serivesmejia/eocvsim/Module.kt | 8 +- .../serivesmejia/eocvsim/config/Config.java | 30 +-- .../eocvsim/config/ConfigLoader.java | 29 +-- .../eocvsim/config/ConfigManager.kt | 21 +- .../serivesmejia/eocvsim/gui/DialogFactory.kt | 45 ++-- .../eocvsim/gui/EOCVSimIconLibrary.kt | 30 +-- .../serivesmejia/eocvsim/gui/IconDelegate.kt | 22 +- .../github/serivesmejia/eocvsim/gui/Icons.kt | 30 +-- .../serivesmejia/eocvsim/gui/Visualizer.kt | 21 +- .../gui/component/input/EnumComboBox.kt | 29 +-- .../gui/component/input/FileSelector.kt | 29 +-- .../eocvsim/gui/component/input/SizeFields.kt | 30 +-- .../gui/component/tuner/ColorPicker.kt | 29 +-- .../gui/component/tuner/TunableFieldPanel.kt | 6 + .../tuner/TunableFieldPanelConfig.kt | 30 +-- .../tuner/TunableFieldPanelOptions.kt | 30 +-- .../tuner/element/TunableComboBox.kt | 6 + .../component/tuner/element/TunableSlider.kt | 30 +-- .../tuner/element/TunableTextField.kt | 6 + .../component/visualizer/CreateSourcePanel.kt | 30 +-- .../visualizer/InputSourceDropTarget.kt | 30 +-- .../gui/component/visualizer/SidebarPanel.kt | 22 +- .../component/visualizer/TelemetryPanel.kt | 29 +-- .../gui/component/visualizer/TopMenuBar.kt | 29 +-- .../visualizer/opmode/OpModeControlsPanel.kt | 30 +-- .../visualizer/opmode/OpModePopupPanel.kt | 30 +-- .../visualizer/opmode/OpModeSelectorPanel.kt | 22 +- .../opmode/SidebarOpModeTabPanel.kt | 30 +-- .../pipeline/PipelineSelectorButtonsPanel.kt | 30 +-- .../pipeline/PipelineSelectorPanel.kt | 29 +-- .../pipeline/SidebarPipelineTabPanel.kt | 30 +-- .../pipeline/SourceSelectorPanel.kt | 29 +-- .../serivesmejia/eocvsim/gui/dialog/About.kt | 21 +- .../eocvsim/gui/dialog/Configuration.kt | 30 +-- .../eocvsim/gui/dialog/CrashReportOutput.kt | 37 +--- .../eocvsim/gui/dialog/CreateWorkspace.kt | 7 +- .../eocvsim/gui/dialog/FileAlreadyExists.kt | 21 +- .../serivesmejia/eocvsim/gui/dialog/Output.kt | 33 +-- .../eocvsim/gui/dialog/PluginOutput.kt | 30 +-- .../eocvsim/gui/dialog/SplashScreen.kt | 35 +-- .../gui/dialog/component/OutputPanel.kt | 38 +--- .../eocvsim/gui/dialog/iama/IAmA.kt | 30 +-- .../gui/dialog/iama/IAmAFirstRobotics.kt | 30 +-- .../gui/dialog/iama/IAmAGeneralPublic.kt | 30 +-- .../gui/dialog/iama/IAmAPaperVision.kt | 30 +-- .../gui/dialog/source/CreateCameraSource.kt | 7 +- .../gui/dialog/source/CreateHttpSource.kt | 29 +-- .../gui/dialog/source/CreateImageSource.kt | 28 +-- .../gui/dialog/source/CreateSourceEx.kt | 30 +-- .../gui/dialog/source/CreateVideoSource.kt | 29 +-- .../serivesmejia/eocvsim/gui/theme/Theme.java | 30 +-- .../eocvsim/gui/theme/ThemeInstaller.java | 29 +-- .../serivesmejia/eocvsim/gui/util/GuiUtil.kt | 21 +- .../eocvsim/gui/util/ThreadedMatPoster.java | 30 +-- .../eocvsim/gui/util/WebcamDriver.kt | 30 +-- .../gui/util/icon/PipelineListIconRenderer.kt | 29 +-- .../util/icon/SourcesListIconRenderer.java | 29 +-- .../serivesmejia/eocvsim/input/InputSource.kt | 21 +- .../eocvsim/input/InputSourceInitializer.kt | 7 +- .../eocvsim/input/InputSourceLoader.kt | 21 +- .../eocvsim/input/InputSourceManager.kt | 21 +- .../serivesmejia/eocvsim/input/SourceType.kt | 21 +- .../eocvsim/input/source/CameraSource.kt | 7 +- .../eocvsim/input/source/HttpSource.kt | 205 ++++-------------- .../eocvsim/input/source/ImageSource.kt | 25 +-- .../eocvsim/input/source/NullSource.kt | 21 +- .../eocvsim/input/source/VideoSource.kt | 26 +-- .../eocvsim/output/RecordingManager.kt | 21 +- .../eocvsim/output/VideoRecordingSession.kt | 30 +-- .../eocvsim/pipeline/DefaultPipeline.java | 29 +-- .../eocvsim/pipeline/PipelineManager.kt | 29 +-- .../compiled/CompiledPipelineManager.kt | 29 +-- .../pipeline/compiled/PipelineClassLoader.kt | 6 + .../pipeline/compiled/PipelineCompiler.kt | 30 +-- .../compiled/PipelineStandardFileManager.kt | 29 +-- .../pipeline/handler/PipelineHandler.kt | 30 +-- .../handler/SpecificPipelineHandler.kt | 30 +-- .../processor/ProcessorInstantiator.kt | 22 +- .../processor/ProcessorPipeline.java | 29 +-- .../pipeline/util/PipelineExceptionTracker.kt | 28 +-- .../eocvsim/pipeline/util/PipelineSnapshot.kt | 29 +-- .../eocvsim/plugin/api/impl/ConfigApiImpl.kt | 30 +-- .../plugin/api/impl/DialogFactoryApiImpl.kt | 30 +-- .../eocvsim/plugin/api/impl/EOCVSimApiImpl.kt | 30 +-- .../api/impl/EventHandlerHookApiImpl.kt | 30 +-- .../plugin/api/impl/InputSourceApisImpl.kt | 30 +-- .../plugin/api/impl/PipelineManagerApiImpl.kt | 30 +-- .../plugin/api/impl/SimpleHookApiImpl.kt | 29 +-- .../eocvsim/plugin/api/impl/SwingApisImpl.kt | 30 +-- .../plugin/api/impl/VariableTunerApiImpl.kt | 30 +-- .../plugin/api/impl/VisualizerApiImpl.kt | 30 +-- .../api/impl/VisualizerComponentsApiImpl.kt | 7 +- .../output/VisualPluginOutputHandler.kt | 30 +-- .../eocvsim/tuner/TunableField.kt | 21 +- .../eocvsim/tuner/TunableFieldAcceptor.kt | 30 +-- .../eocvsim/tuner/TunableFieldRegistry.kt | 21 +- .../eocvsim/tuner/TunableValue.kt | 21 +- .../eocvsim/tuner/TunerManager.kt | 6 + .../CancelTunableFieldAddingException.kt | 30 +-- .../eocvsim/tuner/field/BooleanField.kt | 6 + .../eocvsim/tuner/field/EnumField.kt | 7 +- .../eocvsim/tuner/field/NumericField.kt | 6 + .../eocvsim/tuner/field/StringField.kt | 6 + .../eocvsim/tuner/field/cv/PointField.kt | 6 + .../eocvsim/tuner/field/cv/RectField.kt | 30 +-- .../eocvsim/tuner/field/cv/ScalarField.kt | 6 + .../tuner/field/numeric/DoubleField.kt | 7 +- .../eocvsim/tuner/field/numeric/FloatField.kt | 6 + .../tuner/field/numeric/IntegerField.kt | 6 + .../eocvsim/tuner/field/numeric/LongField.kt | 6 + .../eocvsim/util/ClasspathScan.kt | 22 +- .../eocvsim/util/CombinedRuntimeLoader.java | 4 +- .../serivesmejia/eocvsim/util/FileFilters.kt | 30 +-- .../eocvsim/util/LibraryLoader.java | 9 +- .../eocvsim/util/compiler/CompilerProvider.kt | 30 +-- .../compiler/DelegatingStandardFileManager.kt | 32 +-- .../eocvsim/util/compiler/JarPacker.kt | 30 +-- .../exception/MaxActiveContextsException.kt | 7 +- .../util/exception/handling/CrashReport.kt | 86 ++++---- .../handling/CrashReportOutputMain.kt | 41 +++- .../EOCVSimUncaughtExceptionHandler.kt | 35 ++- .../eocvsim/util/io/FileWatcher.kt | 7 +- .../util/serialization/JacksonJsonSupport.kt | 30 +-- .../util/serialization/VideoModeJackson.kt | 6 + .../eocvsim/workspace/WorkspaceManager.kt | 29 +-- .../workspace/config/WorkspaceConfig.java | 30 +-- .../workspace/config/WorkspaceConfigLoader.kt | 7 +- .../eocvsim/workspace/util/VSCodeLauncher.kt | 30 +-- .../workspace/util/WorkspaceTemplate.kt | 30 +-- .../util/template/DefaultWorkspaceTemplate.kt | 29 +-- .../util/template/GradleWorkspaceTemplate.kt | 29 +-- .../eventloop/opmode/OpModePipelineHandler.kt | 7 +- .../eocvsim/gui/dialog/SuperAccessRequest.kt | 30 +-- .../eocvsim/input/VisionInputSource.kt | 7 +- .../input/VisionInputSourceProvider.kt | 7 +- .../input/control/CameraSourceControlMap.kt | 7 +- .../control/CameraSourceExposureControl.kt | 7 +- .../StreamableOpenCvPipelineInstantiator.kt | 7 +- .../plugin/loader/EmbeddedPluginLoader.kt | 29 +-- .../plugin/loader/FilePluginLoaderImpl.kt | 30 +-- .../plugin/loader/PluginClassLoader.kt | 25 +-- .../eocvsim/plugin/loader/PluginManager.kt | 30 +-- .../repository/PluginRepositoryManager.kt | 30 +-- .../PluginRepositoryUpdateChecker.kt | 25 +-- .../security/superaccess/SuperAccessDaemon.kt | 30 +-- .../superaccess/SuperAccessDaemonClient.kt | 30 +-- .../restrictions/MethodCallByteCodeChecker.kt | 30 +-- .../internal/opmode/EOCVSimTelemetryImpl.java | 6 + .../internal/opmode/TelemetryInternal.java | 7 +- .../opmode/TelemetryTransmissionReceiver.kt | 7 +- .../ProcessFrameInternalAccessor.kt | 30 +-- .../easyopencv/TimestampedPipelineHandler.kt | 30 +-- .../serivesmejia/eocvsim/test/CoreTests.kt | 29 +-- .../external/samples/ConceptAprilTag.java | 7 +- .../external/samples/ConceptAprilTagEasy.java | 7 +- .../samples/ConceptAprilTagLocalization.java | 7 +- .../samples/ConceptVisionColorLocator.java | 22 +- .../samples/ConceptVisionColorSensor.java | 22 +- .../teamcode/AprilTagDetectionPipeline.java | 28 +-- .../SkystoneDeterminationPipeline.java | 28 +-- .../ftc/teamcode/StageSwitchingPipeline.java | 28 +-- .../StoneOrientationAnalysisPipeline.java | 28 +-- .../processor/SimpleThresholdProcessor.java | 29 +-- .../eventloop/opmode/LinearOpMode.java | 6 + .../robotcore/eventloop/opmode/OpMode.java | 6 + .../qualcomm/robotcore/hardware/Gamepad.java | 6 + .../robotcore/hardware/HardwareDevice.java | 7 +- .../robotcore/hardware/HardwareMap.java | 7 +- .../pipeline/StreamableOpenCvPipeline.java | 22 +- .../deltacv/eocvsim/stream/ImageStreamer.kt | 23 +- .../external/FrameReceiverOpenCvCamera.java | 30 +-- .../vision/external/PipelineRenderHook.kt | 29 +-- .../deltacv/vision/external/gui/SkiaPanel.kt | 7 +- .../external/gui/SwingOpenCvViewport.kt | 25 +-- .../vision/external/gui/component/ImageX.java | 29 +-- .../vision/external/gui/util/ImgUtil.java | 6 + .../external/source/CameraControlMap.java | 7 +- .../vision/external/source/FrameReceiver.java | 29 +-- .../source/ThreadVisionSourceProvider.java | 29 +-- .../source/ViewportVisionSourceProvider.java | 29 +-- .../vision/external/source/VisionSource.java | 29 +-- .../external/source/VisionSourceBase.java | 21 +- .../external/source/VisionSourceProvider.java | 30 +-- .../deltacv/vision/external/util/CvUtil.java | 29 +-- .../vision/external/util/FrameQueue.java | 29 +-- .../external/util/ThrowableHandler.java | 6 + .../vision/external/util/Timestamped.java | 29 +-- .../vision/external/util/extension/CvExt.kt | 29 +-- .../deltacv/vision/internal/opmode/Enums.kt | 30 +-- .../vision/internal/opmode/OpModeNotifier.kt | 30 +-- .../RedirectToOpModeThrowableHandler.kt | 30 +-- .../source/ftc/SourcedCameraName.java | 29 +-- .../source/ftc/SourcedCameraNameImpl.java | 29 +-- .../camera/BuiltinCameraDirection.java | 7 +- .../camera/CameraCharacteristics.java | 7 +- .../hardware/camera/CameraControls.java | 7 +- .../external/hardware/camera/CameraName.java | 7 +- .../external/hardware/camera/WebcamName.java | 7 +- .../camera/controls/CameraControl.java | 7 +- .../camera/controls/ExposureControl.java | 6 + .../camera/controls/FocusControl.java | 6 + .../hardware/camera/controls/GainControl.java | 6 + .../hardware/camera/controls/PtzControl.java | 7 +- .../camera/controls/WhiteBalanceControl.java | 6 + .../ftc/vision/VisionPortal.java | 7 +- .../ftc/vision/VisionPortalImpl.java | 7 +- .../ftc/vision/VisionProcessor.java | 6 + .../ftc/vision/VisionProcessorInternal.java | 7 +- .../apriltag/AprilTagCanvasAnnotator.java | 6 + .../vision/apriltag/AprilTagDetection.java | 6 + .../vision/apriltag/AprilTagGameDatabase.java | 6 + .../ftc/vision/apriltag/AprilTagLibrary.java | 6 + .../ftc/vision/apriltag/AprilTagMetadata.java | 6 + .../ftc/vision/apriltag/AprilTagPoseFtc.java | 7 +- .../ftc/vision/apriltag/AprilTagPoseRaw.java | 7 +- .../vision/apriltag/AprilTagProcessor.java | 6 + .../apriltag/AprilTagProcessorImpl.java | 7 +- .../ftc/vision/opencv/Circle.java | 6 + .../opencv/ColorBlobLocatorProcessor.java | 6 + .../opencv/ColorBlobLocatorProcessorImpl.java | 6 + .../ftc/vision/opencv/ColorRange.java | 6 + .../ftc/vision/opencv/ColorSpace.java | 6 + .../ftc/vision/opencv/ImageRegion.java | 6 + .../opencv/PredominantColorProcessor.java | 6 + .../opencv/PredominantColorProcessorImpl.java | 6 + .../main/java/org/opencv/android/Utils.java | 6 + .../org/openftc/easyopencv/OpenCvCamera.java | 28 +-- .../openftc/easyopencv/OpenCvCameraBase.java | 27 +-- .../easyopencv/OpenCvCameraException.java | 27 +-- .../easyopencv/OpenCvCameraFactory.java | 6 + .../easyopencv/OpenCvCameraRotation.java | 28 +-- .../easyopencv/OpenCvInternalCamera.java | 27 +-- .../easyopencv/OpenCvInternalCamera2.java | 27 +-- .../org/openftc/easyopencv/OpenCvTracker.java | 28 +-- .../easyopencv/OpenCvTrackerApiPipeline.java | 28 +-- .../easyopencv/OpenCvViewRenderer.java | 22 +- .../openftc/easyopencv/OpenCvViewport.java | 28 +-- .../org/openftc/easyopencv/OpenCvWebcam.java | 28 +-- .../PipelineRecordingParameters.java | 28 +-- .../SourcedOpenCvCameraFactoryImpl.java | 21 +- .../easyopencv/TimestampedOpenCvPipeline.java | 28 +-- .../java/org/openftc/easyopencv/Util.java | 28 +-- build.gradle | 2 +- 406 files changed, 2418 insertions(+), 5250 deletions(-) diff --git a/.github/workflows/build_ci.yml b/.github/workflows/build_ci.yml index 041a8cb3..dc35e1f4 100644 --- a/.github/workflows/build_ci.yml +++ b/.github/workflows/build_ci.yml @@ -12,7 +12,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: '17' + java-version: '25' - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build and test with Gradle @@ -27,7 +27,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: '17' + java-version: '25' - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build and test with Gradle @@ -42,7 +42,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: '17' + java-version: '25' - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build and test with Gradle diff --git a/.github/workflows/release_ci.yml b/.github/workflows/release_ci.yml index d5449fbf..224bdf88 100644 --- a/.github/workflows/release_ci.yml +++ b/.github/workflows/release_ci.yml @@ -15,7 +15,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: '17' + java-version: '25' - name: Grant execute permission for gradlew run: chmod +x gradlew diff --git a/Common/src/main/java/android/annotation/AnyThread.java b/Common/src/main/java/android/annotation/AnyThread.java index 6c2f7d40..32605c02 100644 --- a/Common/src/main/java/android/annotation/AnyThread.java +++ b/Common/src/main/java/android/annotation/AnyThread.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2016 The Android Open Source Project * @@ -41,4 +46,4 @@ @Retention(CLASS) @Target({METHOD,CONSTRUCTOR,TYPE}) public @interface AnyThread { -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/annotation/ColorInt.java b/Common/src/main/java/android/annotation/ColorInt.java index b6f9e900..9f371a7a 100644 --- a/Common/src/main/java/android/annotation/ColorInt.java +++ b/Common/src/main/java/android/annotation/ColorInt.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2015 The Android Open Source Project * @@ -34,4 +39,4 @@ @Retention(CLASS) @Target({PARAMETER,METHOD,LOCAL_VARIABLE,FIELD}) public @interface ColorInt { -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/annotation/ColorLong.java b/Common/src/main/java/android/annotation/ColorLong.java index 62184800..1bf27efc 100644 --- a/Common/src/main/java/android/annotation/ColorLong.java +++ b/Common/src/main/java/android/annotation/ColorLong.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2016 The Android Open Source Project * @@ -41,4 +46,4 @@ @Retention(SOURCE) @Target({PARAMETER,METHOD,LOCAL_VARIABLE,FIELD}) public @interface ColorLong { -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/annotation/HalfFloat.java b/Common/src/main/java/android/annotation/HalfFloat.java index 69eef7ec..2e75b0c6 100644 --- a/Common/src/main/java/android/annotation/HalfFloat.java +++ b/Common/src/main/java/android/annotation/HalfFloat.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2016 The Android Open Source Project * @@ -42,4 +47,4 @@ @Retention(SOURCE) @Target({PARAMETER, METHOD, LOCAL_VARIABLE, FIELD}) public @interface HalfFloat { -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/annotation/IntDef.java b/Common/src/main/java/android/annotation/IntDef.java index 4fbf3560..7a081c63 100644 --- a/Common/src/main/java/android/annotation/IntDef.java +++ b/Common/src/main/java/android/annotation/IntDef.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2013 The Android Open Source Project * @@ -52,4 +57,4 @@ long[] value() default {}; /** Defines whether the constants can be used as a flag, or just as an enum (the default) */ boolean flag() default false; -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/annotation/IntRange.java b/Common/src/main/java/android/annotation/IntRange.java index 78cd60f7..8b93eb40 100644 --- a/Common/src/main/java/android/annotation/IntRange.java +++ b/Common/src/main/java/android/annotation/IntRange.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2015 The Android Open Source Project * @@ -42,4 +47,4 @@ long from() default Long.MIN_VALUE; /** Largest value, inclusive */ long to() default Long.MAX_VALUE; -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/annotation/NonNull.java b/Common/src/main/java/android/annotation/NonNull.java index a19512e3..33049d7c 100644 --- a/Common/src/main/java/android/annotation/NonNull.java +++ b/Common/src/main/java/android/annotation/NonNull.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2013 The Android Open Source Project * @@ -31,4 +36,4 @@ @Retention(CLASS) @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE}) public @interface NonNull { -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/annotation/Nullable.java b/Common/src/main/java/android/annotation/Nullable.java index 75a9412d..47dc6fd9 100644 --- a/Common/src/main/java/android/annotation/Nullable.java +++ b/Common/src/main/java/android/annotation/Nullable.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2013 The Android Open Source Project @@ -42,4 +47,4 @@ @Target({METHOD, PARAMETER, FIELD}) // @SystemApi(client = Client.MODULE_LIBRARIES) public @interface Nullable { -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/annotation/Size.java b/Common/src/main/java/android/annotation/Size.java index f2de3177..cfa4b3e4 100644 --- a/Common/src/main/java/android/annotation/Size.java +++ b/Common/src/main/java/android/annotation/Size.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2015 The Android Open Source Project * @@ -46,4 +51,4 @@ long max() default Long.MAX_VALUE; /** The size must be a multiple of this factor */ long multiple() default 1; -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/annotation/SuppressAutoDoc.java b/Common/src/main/java/android/annotation/SuppressAutoDoc.java index 3418d658..83eddc97 100644 --- a/Common/src/main/java/android/annotation/SuppressAutoDoc.java +++ b/Common/src/main/java/android/annotation/SuppressAutoDoc.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2017 The Android Open Source Project * @@ -32,4 +37,4 @@ @Retention(SOURCE) @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) public @interface SuppressAutoDoc { -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/annotation/SuppressLint.java b/Common/src/main/java/android/annotation/SuppressLint.java index b51c3f0e..9fd2b196 100644 --- a/Common/src/main/java/android/annotation/SuppressLint.java +++ b/Common/src/main/java/android/annotation/SuppressLint.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2012 The Android Open Source Project * @@ -32,4 +37,4 @@ * ignored by lint. It is not an error to specify an unrecognized name. */ String[] value(); -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/annotation/SystemApi.java b/Common/src/main/java/android/annotation/SystemApi.java index ce3d9bd4..b4ce283e 100644 --- a/Common/src/main/java/android/annotation/SystemApi.java +++ b/Common/src/main/java/android/annotation/SystemApi.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2014 The Android Open Source Project * @@ -71,4 +76,4 @@ enum Client { @interface Container { SystemApi[] value(); } -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/graphics/Bitmap.java b/Common/src/main/java/android/graphics/Bitmap.java index d8cf4e09..c3bfa299 100644 --- a/Common/src/main/java/android/graphics/Bitmap.java +++ b/Common/src/main/java/android/graphics/Bitmap.java @@ -1,24 +1,6 @@ /* * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package android.graphics; @@ -244,3 +226,4 @@ public void recycle() { theBitmap.close(); } } + diff --git a/Common/src/main/java/android/graphics/Canvas.java b/Common/src/main/java/android/graphics/Canvas.java index 87d862bf..d4c35581 100644 --- a/Common/src/main/java/android/graphics/Canvas.java +++ b/Common/src/main/java/android/graphics/Canvas.java @@ -1,24 +1,6 @@ /* * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package android.graphics; @@ -216,4 +198,4 @@ public int getHeight() { return providedHeight; } -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/graphics/Color.java b/Common/src/main/java/android/graphics/Color.java index 02243cc7..fdbeb886 100644 --- a/Common/src/main/java/android/graphics/Color.java +++ b/Common/src/main/java/android/graphics/Color.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2006 The Android Open Source Project * @@ -1480,4 +1485,4 @@ private static int nativeHSVToColor(int alpha, float hsv[]) { sColorNameMap.put("silver", 0xFFC0C0C0); sColorNameMap.put("teal", 0xFF008080); } -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/graphics/ColorSpace.java b/Common/src/main/java/android/graphics/ColorSpace.java index e4559fe2..28b33963 100644 --- a/Common/src/main/java/android/graphics/ColorSpace.java +++ b/Common/src/main/java/android/graphics/ColorSpace.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2016 The Android Open Source Project * @@ -3639,4 +3644,4 @@ public float[] transform(@NonNull @Size(min = 3) float[] v) { }; } } -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/graphics/FontCache.java b/Common/src/main/java/android/graphics/FontCache.java index 71bd9256..283b7a49 100644 --- a/Common/src/main/java/android/graphics/FontCache.java +++ b/Common/src/main/java/android/graphics/FontCache.java @@ -1,24 +1,6 @@ /* * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package android.graphics; @@ -49,3 +31,4 @@ static Font makeFont(Typeface typeface, float textSize) { } } + diff --git a/Common/src/main/java/android/graphics/Paint.java b/Common/src/main/java/android/graphics/Paint.java index 058922b1..2148b797 100644 --- a/Common/src/main/java/android/graphics/Paint.java +++ b/Common/src/main/java/android/graphics/Paint.java @@ -1,30 +1,6 @@ -/* - * Some Original work Copyright (C) 2006 The Android Open Source Project - * Licensed under the Apache License, Version 2.0 (the "License"); - * https://www.apache.org/licenses/LICENSE-2.0 - */ - /* * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package android.graphics; @@ -373,3 +349,4 @@ public float getTextSize() { } } + diff --git a/Common/src/main/java/android/graphics/Path.java b/Common/src/main/java/android/graphics/Path.java index 2db40082..61e709e5 100644 --- a/Common/src/main/java/android/graphics/Path.java +++ b/Common/src/main/java/android/graphics/Path.java @@ -1,30 +1,6 @@ -/* - * Some Original work Copyright (C) 2006 The Android Open Source Project - * Licensed under the Apache License, Version 2.0 (the "License"); - * https://www.apache.org/licenses/LICENSE-2.0 - */ - /* * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package android.graphics; @@ -226,3 +202,4 @@ public void addPath(Path path) { } } + diff --git a/Common/src/main/java/android/graphics/Rect.java b/Common/src/main/java/android/graphics/Rect.java index 3ebea575..08de4465 100644 --- a/Common/src/main/java/android/graphics/Rect.java +++ b/Common/src/main/java/android/graphics/Rect.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2006 The Android Open Source Project * @@ -616,4 +621,4 @@ public void splitHorizontally(@NonNull Rect ...outSplits) { public org.jetbrains.skia.Rect toSkijaRect() { return new org.jetbrains.skia.Rect(left, top, right, bottom); } -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/graphics/TemporaryBuffer.java b/Common/src/main/java/android/graphics/TemporaryBuffer.java index b98e3d9b..83d05938 100644 --- a/Common/src/main/java/android/graphics/TemporaryBuffer.java +++ b/Common/src/main/java/android/graphics/TemporaryBuffer.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package android.graphics; import com.android.internal.util.ArrayUtils; @@ -24,4 +29,4 @@ public static void recycle(char[] temp) { } } private static char[] sTemp = null; -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/graphics/Typeface.java b/Common/src/main/java/android/graphics/Typeface.java index bb9f078d..36f7990e 100644 --- a/Common/src/main/java/android/graphics/Typeface.java +++ b/Common/src/main/java/android/graphics/Typeface.java @@ -1,24 +1,6 @@ /* * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package android.graphics; @@ -71,3 +53,4 @@ private static Data loadDataFromResource(String resource) { } } + diff --git a/Common/src/main/java/android/hardware/Camera.java b/Common/src/main/java/android/hardware/Camera.java index e1811fdd..9c841220 100644 --- a/Common/src/main/java/android/hardware/Camera.java +++ b/Common/src/main/java/android/hardware/Camera.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package android.hardware; public class Camera { @@ -56,3 +61,4 @@ public static class CameraInfo { }; } + diff --git a/Common/src/main/java/android/hardware/DataSpace.java b/Common/src/main/java/android/hardware/DataSpace.java index f8ca324f..aef46610 100644 --- a/Common/src/main/java/android/hardware/DataSpace.java +++ b/Common/src/main/java/android/hardware/DataSpace.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright 2021 The Android Open Source Project * @@ -627,4 +632,4 @@ private DataSpace() {} @DataSpaceRange int range = dataSpace & RANGE_MASK; return range; } -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/opengl/Matrix.java b/Common/src/main/java/android/opengl/Matrix.java index 709339ef..f120bcfc 100644 --- a/Common/src/main/java/android/opengl/Matrix.java +++ b/Common/src/main/java/android/opengl/Matrix.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2007 The Android Open Source Project * @@ -850,4 +855,4 @@ public static void setLookAtM(float[] rm, int rmOffset, translateM(rm, rmOffset, -eyeX, -eyeY, -eyeZ); } -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/util/ArraySet.java b/Common/src/main/java/android/util/ArraySet.java index 0ddae3a1..2218c650 100644 --- a/Common/src/main/java/android/util/ArraySet.java +++ b/Common/src/main/java/android/util/ArraySet.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2013 The Android Open Source Project * @@ -900,4 +905,4 @@ public boolean retainAll(Collection collection) { } return removed; } -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/util/ContainerHelpers.java b/Common/src/main/java/android/util/ContainerHelpers.java index 8ee2ed87..2e8e0124 100644 --- a/Common/src/main/java/android/util/ContainerHelpers.java +++ b/Common/src/main/java/android/util/ContainerHelpers.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2013 The Android Open Source Project * @@ -48,4 +53,4 @@ static int binarySearch(long[] array, int size, long value) { } return ~lo; // value not present } -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/util/Half.java b/Common/src/main/java/android/util/Half.java index f0666394..c557c6f9 100644 --- a/Common/src/main/java/android/util/Half.java +++ b/Common/src/main/java/android/util/Half.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2016 The Android Open Source Project * @@ -844,4 +849,4 @@ public static String toString(@HalfFloat short h) { public static String toHexString(@HalfFloat short h) { return FP16.toHexString(h); } -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/util/MapCollections.java b/Common/src/main/java/android/util/MapCollections.java index eb501515..b0684a4a 100644 --- a/Common/src/main/java/android/util/MapCollections.java +++ b/Common/src/main/java/android/util/MapCollections.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2013 The Android Open Source Project * @@ -484,4 +489,4 @@ public Collection getValues() { protected abstract V colSetValue(int index, V value); protected abstract void colRemoveAt(int index); protected abstract void colClear(); -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/util/Size.java b/Common/src/main/java/android/util/Size.java index b31f94f8..8920d094 100644 --- a/Common/src/main/java/android/util/Size.java +++ b/Common/src/main/java/android/util/Size.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2013 The Android Open Source Project * @@ -137,4 +142,4 @@ public int hashCode() { } private final int mWidth; private final int mHeight; -} \ No newline at end of file +} diff --git a/Common/src/main/java/android/util/SparseIntArray.java b/Common/src/main/java/android/util/SparseIntArray.java index 921b1292..26ff6fd9 100644 --- a/Common/src/main/java/android/util/SparseIntArray.java +++ b/Common/src/main/java/android/util/SparseIntArray.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2006 The Android Open Source Project * @@ -281,4 +286,4 @@ public String toString() { buffer.append('}'); return buffer.toString(); } -} \ No newline at end of file +} diff --git a/Common/src/main/java/androidx/annotation/ColorInt.java b/Common/src/main/java/androidx/annotation/ColorInt.java index ca5942a2..48acc050 100644 --- a/Common/src/main/java/androidx/annotation/ColorInt.java +++ b/Common/src/main/java/androidx/annotation/ColorInt.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2015 The Android Open Source Project * @@ -34,4 +39,4 @@ @Retention(CLASS) @Target({PARAMETER,METHOD,LOCAL_VARIABLE,FIELD}) public @interface ColorInt { -} \ No newline at end of file +} diff --git a/Common/src/main/java/androidx/annotation/NonNull.java b/Common/src/main/java/androidx/annotation/NonNull.java index d3c9d885..82d533e3 100644 --- a/Common/src/main/java/androidx/annotation/NonNull.java +++ b/Common/src/main/java/androidx/annotation/NonNull.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2013 The Android Open Source Project * @@ -33,4 +38,4 @@ @Retention(CLASS) @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE}) public @interface NonNull { -} \ No newline at end of file +} diff --git a/Common/src/main/java/androidx/annotation/Nullable.java b/Common/src/main/java/androidx/annotation/Nullable.java index 33feaf89..898a10e7 100644 --- a/Common/src/main/java/androidx/annotation/Nullable.java +++ b/Common/src/main/java/androidx/annotation/Nullable.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2013 The Android Open Source Project * @@ -39,4 +44,4 @@ @Retention(SOURCE) @Target({METHOD, PARAMETER, FIELD}) public @interface Nullable { -} \ No newline at end of file +} diff --git a/Common/src/main/java/androidx/annotation/StringRes.java b/Common/src/main/java/androidx/annotation/StringRes.java index c6824fc9..9090afab 100644 --- a/Common/src/main/java/androidx/annotation/StringRes.java +++ b/Common/src/main/java/androidx/annotation/StringRes.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2014 The Android Open Source Project * @@ -30,4 +35,4 @@ @Retention(CLASS) @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE}) public @interface StringRes { -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/android/internal/util/ArrayUtils.java b/Common/src/main/java/com/android/internal/util/ArrayUtils.java index 4af45f05..1ff41dda 100644 --- a/Common/src/main/java/com/android/internal/util/ArrayUtils.java +++ b/Common/src/main/java/com/android/internal/util/ArrayUtils.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2006 The Android Open Source Project * @@ -860,4 +865,4 @@ public static List toList(T[] array) { } return list; } -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/android/internal/util/GrowingArrayUtils.java b/Common/src/main/java/com/android/internal/util/GrowingArrayUtils.java index 6ed02cd1..fa512b3b 100644 --- a/Common/src/main/java/com/android/internal/util/GrowingArrayUtils.java +++ b/Common/src/main/java/com/android/internal/util/GrowingArrayUtils.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2014 The Android Open Source Project * @@ -183,4 +188,4 @@ public static int growSize(int currentSize) { } // Uninstantiable private GrowingArrayUtils() {} -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/formdev/flatlaf/demo/HintManager.java b/Common/src/main/java/com/formdev/flatlaf/demo/HintManager.java index 92fd6552..8229475b 100644 --- a/Common/src/main/java/com/formdev/flatlaf/demo/HintManager.java +++ b/Common/src/main/java/com/formdev/flatlaf/demo/HintManager.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright 2020 FormDev Software GmbH * @@ -349,4 +354,4 @@ private Shape createBalloonShape( int width, int height ) { return area; } } -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/component/CollapsiblePanelX.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/component/CollapsiblePanelX.kt index 20a5761d..835c2d96 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/component/CollapsiblePanelX.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/component/CollapsiblePanelX.kt @@ -1,25 +1,6 @@ /* * Copyright (c) 2023 Sebastian Erives - * Credit where it's due - based off of https://stackoverflow.com/a/52956783 - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.gui.component @@ -78,4 +59,4 @@ class CollapsiblePanelX @JvmOverloads constructor( titleText = title toggleButton.text = if (isHidden) "Show $titleText" else "Hide $titleText" } -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/component/PopupX.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/component/PopupX.kt index ea4d9412..db1c6059 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/component/PopupX.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/component/PopupX.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component import com.github.serivesmejia.eocvsim.gui.util.Corner @@ -165,4 +147,4 @@ class PopupX @JvmOverloads constructor(private val windowAncestor: Window, } -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/component/SliderX.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/component/SliderX.kt index bfb5e775..fdd76340 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/component/SliderX.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/component/SliderX.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component import com.qualcomm.robotcore.util.Range @@ -69,4 +51,4 @@ open class SliderX(private var minBound: Double, maximum = this.maxBound.roundToInt() } -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/util/Enums.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/util/Enums.kt index 26ba7737..ff831fb6 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/util/Enums.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/util/Enums.kt @@ -1,6 +1,11 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.util enum class Corner { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/util/ValidCharactersDocumentFilter.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/util/ValidCharactersDocumentFilter.kt index e75e7820..1ddc608d 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/util/ValidCharactersDocumentFilter.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/util/ValidCharactersDocumentFilter.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.util import javax.swing.text.AttributeSet @@ -65,4 +47,4 @@ class ValidCharactersDocumentFilter(val validCharacters: Array) : Document return false } -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/util/extension/SwingExt.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/util/extension/SwingExt.kt index db512e93..5a0eec39 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/util/extension/SwingExt.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/gui/util/extension/SwingExt.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.util.extension import javax.swing.JTextField @@ -38,4 +20,4 @@ var JTextField.documentFilter: DocumentFilter } set(value) { abstractDocument.documentFilter = value - } \ No newline at end of file + } diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/DefaultPipelineInstantiator.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/DefaultPipelineInstantiator.kt index 9fb206bd..7f3050a4 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/DefaultPipelineInstantiator.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/DefaultPipelineInstantiator.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.pipeline.instantiator @@ -43,4 +25,4 @@ object DefaultPipelineInstantiator : PipelineInstantiator { override fun variableTunerTarget(pipeline: OpenCvPipeline) = pipeline -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/PipelineInstantiator.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/PipelineInstantiator.kt index aaaaaa5e..04bc6a73 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/PipelineInstantiator.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/PipelineInstantiator.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.pipeline.instantiator @@ -37,4 +19,4 @@ interface PipelineInstantiator { fun virtualReflectOf(pipeline: OpenCvPipeline): VirtualReflection fun variableTunerTarget(pipeline: OpenCvPipeline): Any? -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/PluginOutputHandler.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/PluginOutputHandler.kt index 46193521..51b861fc 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/PluginOutputHandler.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/PluginOutputHandler.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.plugin.output import com.github.serivesmejia.eocvsim.util.event.ParamEventHandler @@ -111,4 +93,4 @@ interface PluginOutputHandler { * Programmatically signal that continuation is complete. */ fun signalContinuation() -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/JavaProcess.java b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/JavaProcess.java index 383785b0..c67638ca 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/JavaProcess.java +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/JavaProcess.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util; import org.slf4j.Logger; @@ -190,19 +172,19 @@ public static int execClasspath( String classpath, List jvmArgs, List args - ) throws InterruptedException, IOException { - + ) throws IOException { return execClasspathAsync(klass, receiver, classpath, jvmArgs, args) .join(); // wait synchronously } public static int exec(Class klass, List jvmArgs, List args) - throws InterruptedException, IOException { + throws IOException { return execClasspath(klass, null, System.getProperty("java.class.path"), jvmArgs, args); } public static int exec(Class klass, ProcessIOReceiver ioReceiver, List jvmArgs, List args) - throws InterruptedException, IOException { + throws IOException { return execClasspath(klass, ioReceiver, System.getProperty("java.class.path"), jvmArgs, args); } } + diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/ReflectUtil.java b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/ReflectUtil.java index 54906e0f..f88c6b8b 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/ReflectUtil.java +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/ReflectUtil.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util; import java.lang.annotation.Annotation; @@ -111,3 +93,4 @@ public static Class wrap(Class c) { } } + diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/StrUtil.java b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/StrUtil.java index 96f17c31..b95626c9 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/StrUtil.java +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/StrUtil.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util; import java.io.PrintWriter; @@ -136,3 +118,4 @@ public static int editDistance(String s1, String s2) { } + diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/SysUtil.java b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/SysUtil.java index b4f1855f..79528523 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/SysUtil.java +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/SysUtil.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util; import com.github.serivesmejia.eocvsim.util.io.EOCVSimFolder; @@ -374,3 +356,4 @@ public static class CommandResult { } } + diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventHandler.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventHandler.kt index f923cc82..88f2639e 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventHandler.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventHandler.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.event import io.github.deltacv.common.util.loggerOf @@ -263,4 +268,14 @@ class EventHandler @JvmOverloads constructor( object InPlace : CallRightAway object Disabled : CallRightAway } -} \ No newline at end of file + + companion object { + fun batchOnce(vararg eventHandler: EventHandler, listener: OnceEventListener): List { + return eventHandler.map { it.once(listener) } + } + + fun batchAttach(vararg eventHandler: EventHandler, listener: EventListener): List { + return eventHandler.map { it.attach(listener) } + } + } +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventListener.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventListener.kt index 3477752f..a8f2f0c9 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventListener.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventListener.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.util.event @@ -45,4 +27,4 @@ class EventListenerContext( fun removeListener() { handler.removeListener(id) } -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/FileExt.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/FileExt.kt index fa6010bd..a172ea57 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/FileExt.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/FileExt.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.extension import java.io.File @@ -50,4 +32,4 @@ fun byteArrayToHex(bytes: ByteArray): String { sb.append(hex[b.toInt() and 0x0F]) } return sb.toString() -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/NumberExt.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/NumberExt.kt index 194119a5..72b75484 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/NumberExt.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/NumberExt.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.extension /** @@ -37,4 +19,4 @@ fun Int.clipUpperZero(): Int { /** * Clip something like a List size to be an index */ -val Int.zeroBased get() = (this - 1).clipUpperZero() \ No newline at end of file +val Int.zeroBased get() = (this - 1).clipUpperZero() diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/StrExt.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/extension/StrExt.kt index 55bbbd52..36043f20 100644 --- a/Common/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,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.extension import java.security.MessageDigest @@ -20,4 +25,4 @@ val String.hashString: String get() { val messageDigest = MessageDigest.getInstance("SHA-256") val hash = messageDigest.digest(toByteArray()) return byteArrayToHex(hash) -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/fps/FpsCounter.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/fps/FpsCounter.kt index 0f422e99..c22d473e 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/fps/FpsCounter.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/fps/FpsCounter.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.fps import com.qualcomm.robotcore.util.ElapsedTime @@ -55,4 +37,4 @@ class FpsCounter { } } -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/fps/FpsLimiter.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/fps/FpsLimiter.kt index 1c36b284..d8e0110e 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/fps/FpsLimiter.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/fps/FpsLimiter.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.fps class FpsLimiter(var maxFPS: Double = 30.0) { @@ -39,4 +21,4 @@ class FpsLimiter(var maxFPS: Double = 30.0) { start = System.currentTimeMillis().toDouble() } -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/EOCVSimFolder.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/EOCVSimFolder.kt index ede1673a..5a107ce3 100644 --- a/Common/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,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.io import com.github.serivesmejia.eocvsim.util.extension.appData @@ -49,4 +54,4 @@ object EOCVSimFolder : File(appData.absolutePath + separator + ".eocvsim") { } } -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/Lock.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/Lock.kt index 591ad141..67923ab4 100644 --- a/Common/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,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.io import io.github.deltacv.common.util.loggerForThis @@ -100,4 +105,4 @@ fun File.lockDirectory(): LockFile? { return null return lockFile -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrable.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrable.kt index 53707d2b..68cf5bb9 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrable.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrable.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + @file:Suppress("unused") package com.github.serivesmejia.eocvsim.util.orchestration @@ -197,4 +202,4 @@ inline fun PhaseOrchestrable.destroyDependency(target abstract class PhaseOrchestrableBase : PhaseOrchestrable { final override val phaseDependencies: PhaseDependencies = PhaseDependencies() -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrator.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrator.kt index d3f85e4e..35b66eea 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrator.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrator.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + @file:Suppress("unused") package com.github.serivesmejia.eocvsim.util.orchestration @@ -446,3 +451,4 @@ class Orchestrator( } } } + diff --git a/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/Autonomous.java b/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/Autonomous.java index 71447a1c..40e3e333 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/Autonomous.java +++ b/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/Autonomous.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2015 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2015 Robert Atkinson * @@ -72,4 +77,4 @@ * @return see above */ String preselectTeleOp() default ""; -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/Disabled.java b/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/Disabled.java index 398baf99..baa93890 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/Disabled.java +++ b/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/Disabled.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2015 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2015 Robert Atkinson * @@ -52,4 +57,4 @@ @Retention(RetentionPolicy.RUNTIME) public @interface Disabled { -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/TeleOp.java b/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/TeleOp.java index 3e160911..54ead7dc 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/TeleOp.java +++ b/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/TeleOp.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2015 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2015 Robert Atkinson * @@ -63,4 +68,4 @@ * @return the group into which the annotated OpMode is to be categorized */ String group() default ""; -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/qualcomm/robotcore/exception/RobotCoreException.java b/Common/src/main/java/com/qualcomm/robotcore/exception/RobotCoreException.java index 2f983153..e39619a3 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/exception/RobotCoreException.java +++ b/Common/src/main/java/com/qualcomm/robotcore/exception/RobotCoreException.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2014 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2014, 2015 Qualcomm Technologies Inc * @@ -51,4 +56,4 @@ public RobotCoreException(String format, Object... args) { public static RobotCoreException createChained(Exception e, String format, Object... args) { return new RobotCoreException(String.format(format, args), e); } -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/qualcomm/robotcore/util/ElapsedTime.java b/Common/src/main/java/com/qualcomm/robotcore/util/ElapsedTime.java index 850b0944..c91cd709 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/util/ElapsedTime.java +++ b/Common/src/main/java/com/qualcomm/robotcore/util/ElapsedTime.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2014 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2014, 2015 Qualcomm Technologies Inc All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -266,4 +271,4 @@ public enum Resolution { SECONDS, MILLISECONDS } -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/qualcomm/robotcore/util/MovingStatistics.java b/Common/src/main/java/com/qualcomm/robotcore/util/MovingStatistics.java index 5b2ec6f6..70b9b8ed 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/util/MovingStatistics.java +++ b/Common/src/main/java/com/qualcomm/robotcore/util/MovingStatistics.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson All rights reserved. @@ -122,4 +127,4 @@ public void add(double x) this.statistics.remove(this.samples.remove()); } } -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/qualcomm/robotcore/util/Range.java b/Common/src/main/java/com/qualcomm/robotcore/util/Range.java index 16f5eb28..74d59c46 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/util/Range.java +++ b/Common/src/main/java/com/qualcomm/robotcore/util/Range.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2014 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2014, 2015 Qualcomm Technologies Inc @@ -155,4 +160,4 @@ public static void throwIfRangeIsInvalid(int number, int min, int max) throws Il String.format("number %d is invalid; valid ranges are %d..%d", number, min, max)); } } -} \ No newline at end of file +} diff --git a/Common/src/main/java/com/qualcomm/robotcore/util/RobotLog.java b/Common/src/main/java/com/qualcomm/robotcore/util/RobotLog.java index b41530c2..dd25742d 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/util/RobotLog.java +++ b/Common/src/main/java/com/qualcomm/robotcore/util/RobotLog.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.qualcomm.robotcore.util; import org.slf4j.Logger; @@ -15,3 +20,4 @@ public static void ee(String tag, String message) { LoggerFactory.getLogger(tag).error(message); } } + diff --git a/Common/src/main/java/com/qualcomm/robotcore/util/SerialNumber.java b/Common/src/main/java/com/qualcomm/robotcore/util/SerialNumber.java index 158c4ac1..9f1bce68 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/util/SerialNumber.java +++ b/Common/src/main/java/com/qualcomm/robotcore/util/SerialNumber.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2014 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2014, 2015 Qualcomm Technologies Inc All rights reserved. @@ -104,3 +109,4 @@ public int hashCode() { } } + diff --git a/Common/src/main/java/com/qualcomm/robotcore/util/SortOrder.java b/Common/src/main/java/com/qualcomm/robotcore/util/SortOrder.java index bc4fbcf8..3ea3d851 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/util/SortOrder.java +++ b/Common/src/main/java/com/qualcomm/robotcore/util/SortOrder.java @@ -1,22 +1,6 @@ /* - * Copyright (c) 2024 FIRST - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. */ package com.qualcomm.robotcore.util; @@ -26,3 +10,4 @@ public enum SortOrder ASCENDING, DESCENDING } + diff --git a/Common/src/main/java/com/qualcomm/robotcore/util/Statistics.java b/Common/src/main/java/com/qualcomm/robotcore/util/Statistics.java index 520b64f6..9be5e196 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/util/Statistics.java +++ b/Common/src/main/java/com/qualcomm/robotcore/util/Statistics.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson All rights reserved. @@ -137,4 +142,4 @@ public void remove(double x) n = nPrev; } } -} \ No newline at end of file +} diff --git a/Common/src/main/java/dalvik/system/VMRuntime.java b/Common/src/main/java/dalvik/system/VMRuntime.java index b3102c97..b655ccac 100644 --- a/Common/src/main/java/dalvik/system/VMRuntime.java +++ b/Common/src/main/java/dalvik/system/VMRuntime.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package dalvik.system; import java.lang.reflect.Array; @@ -20,3 +25,4 @@ public Object newUnpaddedArray(Class componentType, int length) { } } + diff --git a/Common/src/main/java/io/github/deltacv/common/image/BufferedImageRecycler.java b/Common/src/main/java/io/github/deltacv/common/image/BufferedImageRecycler.java index 76a9d66d..cea14acb 100644 --- a/Common/src/main/java/io/github/deltacv/common/image/BufferedImageRecycler.java +++ b/Common/src/main/java/io/github/deltacv/common/image/BufferedImageRecycler.java @@ -1,24 +1,6 @@ /* * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package io.github.deltacv.common.image; @@ -163,3 +145,4 @@ private RecyclableBufferedImage(int idx, int width, int height, int imageType) { } } + diff --git a/Common/src/main/java/io/github/deltacv/common/image/DynamicBufferedImageRecycler.java b/Common/src/main/java/io/github/deltacv/common/image/DynamicBufferedImageRecycler.java index bb3b0ce3..41c4219b 100644 --- a/Common/src/main/java/io/github/deltacv/common/image/DynamicBufferedImageRecycler.java +++ b/Common/src/main/java/io/github/deltacv/common/image/DynamicBufferedImageRecycler.java @@ -1,24 +1,6 @@ /* * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package io.github.deltacv.common.image; @@ -90,4 +72,4 @@ public synchronized void flushAll() { } } -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/common/image/MatPoster.java b/Common/src/main/java/io/github/deltacv/common/image/MatPoster.java index d012753b..295d638e 100644 --- a/Common/src/main/java/io/github/deltacv/common/image/MatPoster.java +++ b/Common/src/main/java/io/github/deltacv/common/image/MatPoster.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.common.image; import org.opencv.core.Mat; @@ -23,3 +28,4 @@ default void post(Mat m) { void post(Mat m, Object context); } + diff --git a/Common/src/main/java/io/github/deltacv/common/pipeline/util/PipelineStatisticsCalculator.kt b/Common/src/main/java/io/github/deltacv/common/pipeline/util/PipelineStatisticsCalculator.kt index 4a9c29cd..9ac50466 100644 --- a/Common/src/main/java/io/github/deltacv/common/pipeline/util/PipelineStatisticsCalculator.kt +++ b/Common/src/main/java/io/github/deltacv/common/pipeline/util/PipelineStatisticsCalculator.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.common.pipeline.util import com.qualcomm.robotcore.util.ElapsedTime @@ -95,4 +100,4 @@ class PipelineStatisticsCalculator { avgOverheadTime = avgTotalFrameTime - avgPipelineTime } -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/common/util/LoggerDelegates.kt b/Common/src/main/java/io/github/deltacv/common/util/LoggerDelegates.kt index c5ce422e..e409c2c1 100644 --- a/Common/src/main/java/io/github/deltacv/common/util/LoggerDelegates.kt +++ b/Common/src/main/java/io/github/deltacv/common/util/LoggerDelegates.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.common.util import org.slf4j.LoggerFactory @@ -13,4 +18,4 @@ fun Any.loggerForThis() = lazy { fun loggerOf(name: String) = lazy { LoggerFactory.getLogger(name)!! -} \ No newline at end of file +} 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 b6b6cc06..7eec93bc 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 @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.common.util /** @@ -45,4 +50,4 @@ class ParsedVersion(val version: String) { return "$major.$minor.$patch" } -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/EOCVSimPlugin.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/EOCVSimPlugin.kt index 8ef7b4dd..827bae45 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/EOCVSimPlugin.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/EOCVSimPlugin.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin import io.github.deltacv.common.util.loggerForThis @@ -114,3 +119,4 @@ abstract class EOCVSimPlugin { */ abstract fun onDisable() } + 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 index 5f40c372..00ad26b1 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/Folders.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/Folders.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin import com.github.serivesmejia.eocvsim.util.extension.plus @@ -30,4 +12,4 @@ import java.io.File val PLUGIN_FOLDER get() = (EOCVSimFolder + File.separator + "plugins").apply { mkdir() } val PLUGIN_CACHING_FOLDER get() = (PLUGIN_FOLDER + File.separator + "caching").apply { mkdir() } val EMBEDDED_PLUGIN_FOLDER get() = (PLUGIN_CACHING_FOLDER + File.separator + "embedded").apply { mkdir() } -val FILESYSTEMS_FOLDER get() = (PLUGIN_FOLDER + File.separator + "filesystems").apply { mkdir() } \ No newline at end of file +val FILESYSTEMS_FOLDER get() = (PLUGIN_FOLDER + File.separator + "filesystems").apply { mkdir() } diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/Api.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/Api.kt index c80a9504..0bd8292d 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/Api.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/Api.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.api import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin @@ -272,4 +254,4 @@ abstract class Api(val owner: EOCVSimPlugin) { @JvmStatic protected fun nullableApiField(value: T?): ReadOnlyProperty = nullableApiField { value } } -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/ApiDisabler.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/ApiDisabler.kt index b6d77081..731bbe81 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/ApiDisabler.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/ApiDisabler.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.api /** @@ -37,4 +19,4 @@ object ApiDisabler { fun disableApis(vararg apis: Api) { apis.forEach { it.internalDisableApi() } } -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/ConfigApi.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/ConfigApi.kt index 548be1ab..a00e4e43 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/ConfigApi.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/ConfigApi.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.api import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin @@ -70,4 +52,4 @@ abstract class ConfigApi(owner: EOCVSimPlugin) : Api(owner) { * @return `true` if the flag is set, `false` otherwise */ abstract fun hasFlag(flag: String): Boolean -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/DialogFactoryApi.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/DialogFactoryApi.kt index c93ebfbe..5739eb6e 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/DialogFactoryApi.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/DialogFactoryApi.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.api import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin @@ -166,4 +148,4 @@ abstract class DialogFactoryApi(owner: EOCVSimPlugin) : Api(owner) { * @param report the crash report content */ abstract fun createCrashReportDialog(report: String) -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/EOCVSimApi.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/EOCVSimApi.kt index 9ca9e4f6..7190ba76 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/EOCVSimApi.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/EOCVSimApi.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.api import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin @@ -66,3 +48,4 @@ abstract class EOCVSimApi(owner: EOCVSimPlugin) : Api(owner) { */ abstract val configApi: ConfigApi } + diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/HookApi.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/HookApi.kt index 3c661429..0c3dcbcd 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/HookApi.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/HookApi.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.api import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin @@ -92,3 +74,4 @@ abstract class HookApi(owner: EOCVSimPlugin) : Api(owner) { fun detach() } } + diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/InputSourceApis.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/InputSourceApis.kt index d9db7908..5f10138a 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/InputSourceApis.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/InputSourceApis.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.api import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin @@ -176,4 +158,4 @@ abstract class InputSourceManagerApi(owner: EOCVSimPlugin) : Api(owner) { name: String, aNew: InputSourceApi.New ): InputSourceApi -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/PipelineManagerApi.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/PipelineManagerApi.kt index 62014d36..4166e095 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/PipelineManagerApi.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/PipelineManagerApi.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.api import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin @@ -306,4 +288,4 @@ abstract class PipelineManagerApi(owner: EOCVSimPlugin) : Api(owner) { USER_REQUESTED, IMAGE_SINGLE_SHOT } -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/SwingApis.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/SwingApis.kt index 8d8148bb..f917ea36 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/SwingApis.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/SwingApis.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.api import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin @@ -138,4 +120,4 @@ abstract class JFileChooserApi(owner: EOCVSimPlugin) : Api(owner) { /** An error occurred */ ERROR } -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VariableTunerApi.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VariableTunerApi.kt index 35be3de0..53289335 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VariableTunerApi.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VariableTunerApi.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.api import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin @@ -86,4 +68,4 @@ abstract class VariableTunerApi(owner: EOCVSimPlugin) : Api(owner) { * @return the matching tunable field, or null if none exists */ abstract fun getTunableFieldWithLabel(label: String): TunableFieldApi? -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerApi.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerApi.kt index b5696ba8..aa6edea8 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerApi.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerApi.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.api import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin @@ -193,4 +175,4 @@ abstract class VisualizerViewportApi(owner: EOCVSimPlugin) : Api(owner) { abstract fun deactivate() abstract fun setFpsMeterEnabled(enabled: Boolean) -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerComponentsApi.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerComponentsApi.kt index 14c06901..cd0716a5 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerComponentsApi.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerComponentsApi.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.api import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin @@ -157,4 +162,4 @@ abstract class TelemetryPanelApi(owner: EOCVSimPlugin) : Api(owner) { * Clears all telemetry data from the panel. */ abstract fun clear() -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/exception/EOCVSimApiException.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/exception/EOCVSimApiException.kt index d9c97cd8..050d63c2 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/exception/EOCVSimApiException.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/exception/EOCVSimApiException.kt @@ -1,28 +1,10 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.api.exception import io.github.deltacv.eocvsim.plugin.api.Api -class EOCVSimApiException(message: String, val api: Api) : RuntimeException("Exception thrown by API ${api::class.simpleName} $message") \ No newline at end of file +class EOCVSimApiException(message: String, val api: Api) : RuntimeException("Exception thrown by API ${api::class.simpleName} $message") diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Exceptions.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Exceptions.kt index 9a68df9e..7400cc6e 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Exceptions.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Exceptions.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package io.github.deltacv.eocvsim.plugin.loader @@ -27,4 +9,4 @@ import kotlin.RuntimeException class InvalidPluginException(message: String, cause: Throwable? = null) : RuntimeException(message, cause) -class UnsupportedPluginException(message: String) : RuntimeException(message) \ No newline at end of file +class UnsupportedPluginException(message: String) : RuntimeException(message) diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoader.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoader.kt index b3d6f4e7..551e30d3 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoader.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoader.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.loader import java.io.File @@ -40,4 +22,4 @@ abstract class FilePluginLoader : PluginLoader() { * plugin source supported by the loader implementation. */ abstract val pluginFile: File -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginContext.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginContext.kt index a81b76c6..0b62ec5d 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginContext.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginContext.kt @@ -1,25 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + @file:Suppress("unused") package io.github.deltacv.eocvsim.plugin.loader @@ -276,4 +259,4 @@ constructor( * @param reason a human-readable explanation of why elevated access is required */ fun requestSuperAccess(reason: String) = loader.requestSuperAccess(reason) -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginLoader.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginLoader.kt index 37078719..a8e9a9ab 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginLoader.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginLoader.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.loader import com.github.serivesmejia.eocvsim.util.extension.hashString @@ -179,3 +161,4 @@ abstract class PluginLoader { */ fun hash(): String = pluginInfo.hash() } + diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Providers.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Providers.kt index 3abcdb4e..9085325d 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Providers.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Providers.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.loader import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin @@ -54,4 +36,4 @@ fun interface EOCVSimApiProvider { * @param plugin the plugin requesting access to the API */ fun provideEOCVSimApiFor(plugin: EOCVSimPlugin): EOCVSimApi -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt index be3fd5b6..f707338c 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.security import com.github.serivesmejia.eocvsim.util.extension.plus @@ -28,11 +10,11 @@ import com.github.serivesmejia.eocvsim.util.io.LockFile import io.github.deltacv.common.util.loggerForThis import io.github.deltacv.eocvsim.plugin.PLUGIN_CACHING_FOLDER import java.io.File -import java.net.URL +import java.net.URI import java.security.KeyFactory import java.security.PublicKey import java.security.spec.X509EncodedKeySpec -import java.util.Base64 +import java.util.* import java.util.concurrent.TimeUnit data class Authority( @@ -107,12 +89,12 @@ object AuthorityFetcher { } // Fetch the authority from the server - val authorityUrl = "${AUTHORITY_SERVER_URL.trim('/')}/${name}.txt" + val authorityUrl = "${AUTHORITY_SERVER_URL.trim('/')}/${name}" return try { logger.info("Fetching authority from URL: $authorityUrl") - val authorityPublicKey = URL(authorityUrl).readText() + val authorityPublicKey = URI.create(authorityUrl).toURL().readText() val pem = parsePem(authorityPublicKey) val authority = Authority(name, parsePublicKey(pem)) @@ -211,4 +193,4 @@ object AuthorityFetcher { val keyFactory = KeyFactory.getInstance("RSA") return keyFactory.generatePublic(keySpec) } -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt index adbaf8ef..f21e8103 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.security import java.io.File @@ -59,4 +41,4 @@ fun saveKeyToFile(filename: String, key: java.security.Key) { FileWriter(File(filename)).use { writer -> writer.write(pemFormat) } -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt index 8f2822fb..caa79e46 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.security import com.github.serivesmejia.eocvsim.util.extension.hashString @@ -178,4 +160,4 @@ object PluginSignatureVerifier { // Decode the Base64-encoded string into a byte array return Base64.getDecoder().decode(cleanSignature) } -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSigningTool.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSigningTool.kt index 47114cf6..4db0582a 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSigningTool.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSigningTool.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.security import com.github.serivesmejia.eocvsim.util.extension.hashString @@ -231,4 +213,4 @@ class PluginSigningTool : Runnable { } } } -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/sandbox/nio/SandboxFileSystem.kt b/Common/src/main/java/io/github/deltacv/eocvsim/sandbox/nio/SandboxFileSystem.kt index 92cde2e8..9622a8ef 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/sandbox/nio/SandboxFileSystem.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/sandbox/nio/SandboxFileSystem.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package io.github.deltacv.eocvsim.sandbox.nio @@ -149,4 +131,4 @@ class SandboxFileSystem( Files.createDirectories(dir, *attrs) } -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/DynamicCodeRestrictions.kt b/Common/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/DynamicCodeRestrictions.kt index 6cca7af7..846f723a 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/DynamicCodeRestrictions.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/DynamicCodeRestrictions.kt @@ -1,16 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.sandbox.restrictions /** @@ -322,3 +314,4 @@ val dynamicCodeMethodBlacklist = setOf( "java.io.File#isHidden", "java.io.File#lastModified" ) + diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualField.kt b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualField.kt index b3124ab7..cdc8661b 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualField.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualField.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2022 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package io.github.deltacv.eocvsim.virtualreflect @@ -44,4 +26,4 @@ interface VirtualField { enum class Visibility { PUBLIC, PROTECTED, PRIVATE, PACKAGE_PRIVATE -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflectContext.kt b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflectContext.kt index 059db68c..566a80a5 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflectContext.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflectContext.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2022 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package io.github.deltacv.eocvsim.virtualreflect @@ -54,4 +36,4 @@ interface VirtualReflectContext { */ fun getLabeledField(label: String): VirtualField? -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflection.kt b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflection.kt index f4f414bf..19df43d2 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflection.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflection.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2022 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package io.github.deltacv.eocvsim.virtualreflect @@ -32,4 +14,4 @@ interface VirtualReflection { fun contextOf(value: Any): VirtualReflectContext? -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualField.kt b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualField.kt index aea7811c..3b3256ad 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualField.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualField.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2022 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package io.github.deltacv.eocvsim.virtualreflect.jvm @@ -70,4 +52,4 @@ class JvmVirtualField( field.set(instance, value) } -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflectContext.kt b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflectContext.kt index fb2c6f9b..22c6615f 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflectContext.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflectContext.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2022 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package io.github.deltacv.eocvsim.virtualreflect.jvm @@ -65,4 +47,4 @@ class JvmVirtualReflectContext( return cachedVirtualFields[field]!! } -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflection.kt b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflection.kt index 1053279e..d150ee6b 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflection.kt +++ b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflection.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2022 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package io.github.deltacv.eocvsim.virtualreflect.jvm @@ -44,4 +26,4 @@ object JvmVirtualReflection : VirtualReflection { return cache[value]!!.get()!! } -} \ No newline at end of file +} diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/Label.java b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/Label.java index 0b050c6b..2b9ceba3 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/Label.java +++ b/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/Label.java @@ -1,24 +1,6 @@ /* * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package io.github.deltacv.eocvsim.virtualreflect.jvm; @@ -36,3 +18,4 @@ public @interface Label { String name(); } + diff --git a/Common/src/main/java/libcore/util/EmptyArray.java b/Common/src/main/java/libcore/util/EmptyArray.java index acb9f6ed..a4ecd4cc 100644 --- a/Common/src/main/java/libcore/util/EmptyArray.java +++ b/Common/src/main/java/libcore/util/EmptyArray.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2010 The Android Open Source Project * @@ -69,4 +74,4 @@ private EmptyArray() {} new java.lang.reflect.TypeVariable[0]; /** @hide */ public static final Annotation[] ANNOTATION = new Annotation[0]; -} \ No newline at end of file +} diff --git a/Common/src/main/java/libcore/util/FP16.java b/Common/src/main/java/libcore/util/FP16.java index 69a3c996..8452c803 100644 --- a/Common/src/main/java/libcore/util/FP16.java +++ b/Common/src/main/java/libcore/util/FP16.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2019 The Android Open Source Project * @@ -791,4 +796,4 @@ public static String toHexString(short h) { } return o.toString(); } -} \ No newline at end of file +} diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Const.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Const.java index 97652a60..b9823e38 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Const.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Const.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -52,3 +57,4 @@ are permitted (subject to the limitations in the disclaimer below) provided that public @interface Const { } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Func.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Func.java index 3737d2fe..03cff183 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Func.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Func.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package org.firstinspires.ftc.robotcore.external; public interface Func { @@ -5,3 +10,4 @@ public interface Func { T value(); } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/NonConst.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/NonConst.java index 36f8dffb..ad6db054 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/NonConst.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/NonConst.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -52,3 +57,4 @@ are permitted (subject to the limitations in the disclaimer below) provided that public @interface NonConst { } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Predicate.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Predicate.java index 1ee8f137..978a06b6 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Predicate.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Predicate.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2017 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2017 Robert Atkinson All rights reserved. @@ -29,4 +34,4 @@ are permitted (subject to the limitations in the disclaimer below) provided that public interface Predicate { boolean test(T t); -} \ No newline at end of file +} diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Telemetry.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Telemetry.java index b15b12e9..aead5c69 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Telemetry.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Telemetry.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -630,3 +635,4 @@ enum DisplayOrder { NEWEST_FIRST, OLDEST_FIRST } */ Log log(); } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/android/util/Size.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/android/util/Size.java index 095b9e23..c88e00ae 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/android/util/Size.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/android/util/Size.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (C) 2013 The Android Open Source Project * @@ -149,4 +154,4 @@ public int hashCode() { private final int mWidth; private final int mHeight; -} \ No newline at end of file +} diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/function/Consumer.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/function/Consumer.java index 640cf070..bfc4d1ca 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/function/Consumer.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/function/Consumer.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson All rights reserved. @@ -36,4 +41,4 @@ public interface Consumer { * @param value the input argument */ void accept(T value); -} \ No newline at end of file +} diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/ColumnMajorMatrixF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/ColumnMajorMatrixF.java index 1d6f5f22..356622d6 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/ColumnMajorMatrixF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/ColumnMajorMatrixF.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -54,3 +59,4 @@ public ColumnMajorMatrixF(int nRows, int nCols) return new VectorF(this.getData()); } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/ColumnMatrixF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/ColumnMatrixF.java index 79e845f8..e5cbab7c 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/ColumnMatrixF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/ColumnMatrixF.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -60,3 +65,4 @@ public ColumnMatrixF(VectorF vector) return new GeneralMatrixF(numRows, numCols); } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/DenseMatrixF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/DenseMatrixF.java index 396bd97a..1161ce0e 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/DenseMatrixF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/DenseMatrixF.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -73,3 +78,4 @@ protected DenseMatrixF(int nRows, int nCols) */ protected abstract int indexFromRowCol(int row, int col); } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/GeneralMatrixF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/GeneralMatrixF.java index 3e86df07..466d336e 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/GeneralMatrixF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/GeneralMatrixF.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -73,3 +78,4 @@ public GeneralMatrixF transposed() return (GeneralMatrixF)super.transposed(); } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/MatrixF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/MatrixF.java index bfbd11f3..6bfd9edf 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/MatrixF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/MatrixF.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -801,4 +806,4 @@ protected static RuntimeException dimensionsError(int numRows, int numCols) throw dimensionsError(); // really NYI: we haven't bothered to code other cases } -} \ No newline at end of file +} diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/OpenGLMatrix.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/OpenGLMatrix.java index 277867f9..ce99460a 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/OpenGLMatrix.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/OpenGLMatrix.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -262,3 +267,4 @@ public static OpenGLMatrix identityMatrix() super.multiply(him); } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/RowMajorMatrixF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/RowMajorMatrixF.java index 9a36052a..27758d2c 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/RowMajorMatrixF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/RowMajorMatrixF.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -55,3 +60,4 @@ protected int indexFromRowCol(int row, int col) return new VectorF(this.getData()); } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/RowMatrixF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/RowMatrixF.java index 86b5cee8..0d826f49 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/RowMatrixF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/RowMatrixF.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -60,3 +65,4 @@ public RowMatrixF(VectorF vector) return new GeneralMatrixF(numRows, numCols); } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/SliceMatrixF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/SliceMatrixF.java index d85b9d9e..dafec28b 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/SliceMatrixF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/SliceMatrixF.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -88,3 +93,4 @@ public SliceMatrixF(MatrixF matrix, int row, int col, int numRows, int numCols) return this.matrix.emptyMatrix(numRows, numCols); } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/VectorF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/VectorF.java index 67ae41ba..98972b45 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/VectorF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/VectorF.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -316,3 +321,4 @@ protected RuntimeException dimensionsError() return new IllegalArgumentException(String.format("vector dimensions are incorrect: length=%d", length)); } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Acceleration.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Acceleration.java index da450693..541d4a05 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Acceleration.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Acceleration.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -118,3 +123,4 @@ public Acceleration toUnit(DistanceUnit distanceUnit) return String.format(Locale.getDefault(), "(%.3f %.3f %.3f)%s/s^2", xAccel, yAccel, zAccel, unit.toString()); } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AngleUnit.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AngleUnit.java index 39150c91..9b288fcd 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AngleUnit.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AngleUnit.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -237,3 +242,4 @@ public UnnormalizedAngleUnit getUnnormalized() } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AxesOrder.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AxesOrder.java index 4bb3f743..6fd28c74 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AxesOrder.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AxesOrder.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -100,4 +105,4 @@ public AxesOrder reverse() case ZXY: return YXZ; } } -} \ No newline at end of file +} diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AxesReference.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AxesReference.java index bc34ce26..358ecfc9 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AxesReference.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AxesReference.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -55,3 +60,4 @@ public AxesReference reverse() } } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Axis.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Axis.java index a8064ecb..011ba401 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Axis.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Axis.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -57,3 +62,4 @@ public static Axis fromIndex(int index) } } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/DistanceUnit.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/DistanceUnit.java index e44675c3..5d6da5d8 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/DistanceUnit.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/DistanceUnit.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -200,3 +205,4 @@ public String toString(double inOurUnits) } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Orientation.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Orientation.java index c5899e5b..3cf68727 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Orientation.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Orientation.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -884,4 +889,4 @@ else if (test == 1) 0); } -} \ No newline at end of file +} diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Pose3D.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Pose3D.java index 9e68bedd..8fddc08b 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Pose3D.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Pose3D.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2024 Dryw Wade * @@ -87,3 +92,4 @@ public Position getPosition() return position; } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Position.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Position.java index 6d69652a..27ce3a42 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Position.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Position.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -99,3 +104,4 @@ public Position toUnit(DistanceUnit distanceUnit) return String.format(Locale.getDefault(), "(%.3f %.3f %.3f)%s", x, y, z, unit.toString()); } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Quaternion.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Quaternion.java index 0dcef9a5..9187b05a 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Quaternion.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Quaternion.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -298,3 +303,4 @@ public String toString() return String.format(Locale.US, "{w=%.3f, x=%.3f, y=%.3f, z=%.3f}", w, x, y, z); } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/UnnormalizedAngleUnit.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/UnnormalizedAngleUnit.java index 5679223c..2989385e 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/UnnormalizedAngleUnit.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/UnnormalizedAngleUnit.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -195,3 +200,4 @@ public AngleUnit getNormalized() } } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Velocity.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Velocity.java index e8b0009b..12c1e658 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Velocity.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Velocity.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -102,3 +107,4 @@ public Velocity toUnit(DistanceUnit distanceUnit) return String.format(Locale.getDefault(), "(%.3f %.3f %.3f)%s/s", xVeloc, yVeloc, zVeloc, unit.toString()); } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/YawPitchRollAngles.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/YawPitchRollAngles.java index 328362fa..6c70633c 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/YawPitchRollAngles.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/YawPitchRollAngles.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2022 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2022 REV Robotics @@ -142,3 +147,4 @@ public String toString() { angleUnit.toDegrees(yaw), angleUnit.toDegrees(pitch), angleUnit.toDegrees(roll)); } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraCalibration.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraCalibration.java index a2180236..8b81bce8 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraCalibration.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraCalibration.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2018 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2018 Robert Atkinson @@ -172,3 +177,4 @@ protected static double getAspectRatio(Size size) } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraCalibrationIdentity.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraCalibrationIdentity.java index 33e73d9d..dad7520e 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraCalibrationIdentity.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraCalibrationIdentity.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2018 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2018 Robert Atkinson @@ -35,4 +40,4 @@ are permitted (subject to the limitations in the disclaimer below) provided that public interface CameraCalibrationIdentity { boolean isDegenerate(); -} \ No newline at end of file +} diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraIntrinsics.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraIntrinsics.java index 06004e24..bd1e6793 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraIntrinsics.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraIntrinsics.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2018 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2018 Robert Atkinson @@ -114,4 +119,4 @@ public boolean isDegenerate() && distortionCoefficients[7]==0; } -} \ No newline at end of file +} diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/VendorProductCalibrationIdentity.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/VendorProductCalibrationIdentity.java index c88f775b..0c3ee9c1 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/VendorProductCalibrationIdentity.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/VendorProductCalibrationIdentity.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2018 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2018 Robert Atkinson @@ -82,3 +87,4 @@ public VendorProductCalibrationIdentity(int vid, int pid) return Integer.valueOf(vid).hashCode() ^ Integer.valueOf(pid).hashCode() ^ 738187; } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/collections/EvictingBlockingQueue.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/collections/EvictingBlockingQueue.java index 895a2004..97ef2f3d 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/collections/EvictingBlockingQueue.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/collections/EvictingBlockingQueue.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson All rights reserved. @@ -212,4 +217,4 @@ public void clear() { targetQueue.clear(); } } -} \ No newline at end of file +} diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/collections/MutableReference.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/collections/MutableReference.java index eaf9e077..12393c8f 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/collections/MutableReference.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/collections/MutableReference.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2017 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2017 Robert Atkinson @@ -66,3 +71,4 @@ public T getValue() return "[{" + getValue() + "}]"; } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/AppUtil.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/AppUtil.java index 1d7171a0..65a19108 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/AppUtil.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/AppUtil.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package org.firstinspires.ftc.robotcore.internal.system; import com.qualcomm.robotcore.util.RobotLog; @@ -68,3 +73,4 @@ public void exitApplication(int code) { System.exit(code); } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/Assert.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/Assert.java index 954b111d..b50e9282 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/Assert.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/Assert.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson All rights reserved. @@ -116,4 +121,4 @@ public static void assertFailed(String format, Object[] args) { logger.error(banner, e); } } -} \ No newline at end of file +} diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/Misc.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/Misc.java index 9b915ac4..772b3a5e 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/Misc.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/Misc.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2017 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2017 Robert Atkinson @@ -472,4 +477,4 @@ public static RuntimeException internalError(Throwable throwable, String message { return new RuntimeException("internal error:" + message, throwable); } -} \ No newline at end of file +} diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/robocol/TelemetryMessage.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/robocol/TelemetryMessage.java index 41a62006..2b0c9992 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/robocol/TelemetryMessage.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/robocol/TelemetryMessage.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package org.firstinspires.ftc.robotcore.robocol; /** @@ -20,3 +25,4 @@ public class TelemetryMessage { public final static int cbValueMax = (1 << (cbValueLen*8)) - 1; } + diff --git a/Common/src/main/java/org/openftc/easyopencv/MatRecycler.java b/Common/src/main/java/org/openftc/easyopencv/MatRecycler.java index cea5a597..dad3448b 100644 --- a/Common/src/main/java/org/openftc/easyopencv/MatRecycler.java +++ b/Common/src/main/java/org/openftc/easyopencv/MatRecycler.java @@ -1,24 +1,8 @@ -/* - * Copyright (c) 2019 OpenFTC Team - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - +/* + * Copyright (c) 2019 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv; import org.opencv.core.CvType; @@ -151,3 +135,4 @@ public void copyTo(Mat mat) { } } } + diff --git a/Common/src/main/java/org/openftc/easyopencv/OpenCvPipeline.java b/Common/src/main/java/org/openftc/easyopencv/OpenCvPipeline.java index 2daee177..07bb8da3 100644 --- a/Common/src/main/java/org/openftc/easyopencv/OpenCvPipeline.java +++ b/Common/src/main/java/org/openftc/easyopencv/OpenCvPipeline.java @@ -1,22 +1,6 @@ /* - * Copyright (c) 2019 OpenFTC Team - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Copyright (c) 2019 Sebastian Erives + * Licensed under the MIT License. */ package org.openftc.easyopencv; @@ -91,4 +75,4 @@ Mat processFrameInternal(Mat input) return processFrame(input); } -} \ No newline at end of file +} diff --git a/EOCV-Sim/build.gradle b/EOCV-Sim/build.gradle index ed7367cb..208f5490 100644 --- a/EOCV-Sim/build.gradle +++ b/EOCV-Sim/build.gradle @@ -28,6 +28,7 @@ sourceSets { shadowJar { mergeServiceFiles() + archiveClassifier.set(wpilibTools.currentPlatform.platformName) } test { 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 1652732d..7d7a00ce 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 @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim import com.github.serivesmejia.eocvsim.config.Config @@ -35,6 +17,7 @@ import com.github.serivesmejia.eocvsim.tuner.TunerManager import com.github.serivesmejia.eocvsim.util.JavaProcess import com.github.serivesmejia.eocvsim.util.LibraryLoader import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.util.exception.handling.CrashReport import com.github.serivesmejia.eocvsim.util.exception.handling.EOCVSimUncaughtExceptionHandler import com.github.serivesmejia.eocvsim.util.fps.FpsLimiter import com.github.serivesmejia.eocvsim.util.io.EOCVSimFolder @@ -50,6 +33,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.swing.Swing import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.koin.core.qualifier.named @@ -88,8 +74,6 @@ class EOCVSim : KoinComponent { } } - val parameters: Parameters by inject() - val orchestrator: Orchestrator by inject() /** @@ -99,7 +83,8 @@ class EOCVSim : KoinComponent { * posted by the different components of the simulator * @see EventHandler */ - val onMainLoop: EventHandler by inject(named("onMainLoop")) + private val onMainLoop: EventHandler by inject(named("onMainLoop")) + private val onCrash: EventHandler by inject(named("onCrash")) /** * The visualizer instance in charge of managing the GUI @@ -193,7 +178,7 @@ class EOCVSim : KoinComponent { logger.info("Confirmed claiming of the lock file in ${EOCVSimFolder.absolutePath}") } - dialogFactory.createSplashScreen(visualizer.onInitFinished) + dialogFactory.createSplashScreen(visualizer.onInitFinished, onCrash) logger.info("-- Initializing EasyOpenCV Simulator v$VERSION ($hexCode) --") @@ -204,6 +189,25 @@ class EOCVSim : KoinComponent { if(!loadLibrariesResult.success) { logger.error("Exception in loadLibraries():", loadLibrariesResult.error) logger.error("The sim will exit now as it's impossible to continue without the required libraries") + + onCrash.run() + + runBlocking { + launch(Dispatchers.Swing) { + val crashHeader = """ + One or more of WPILib's native libraries failed to load at the earliest stage. + Ensure you're running EOCV-Sim on a supported platform, Java 25 is required. + Read the crash report below, this is not likely to be a bug on the program, + If it seems like a bug, please open an issue in GitHub with this report. + """.trimIndent() + + dialogFactory.instantiateCrashReport( + crash = "$crashHeader\n\n${CrashReport(loadLibrariesResult.error())}", + headerText = "An error occurred while loading the required native libraries for your platform" + ) + } + } + exitProcess(-1) } @@ -394,14 +398,13 @@ class EOCVSim : KoinComponent { */ class Parameters { /** - * The initial user workspace file to load - * Overrides the workspace file in the config file + * The initial user workspace path to load + * Overrides the workspace path in the config */ var initialWorkspace: File? = null /** - * The initial pipeline name to load - * specified by class name + * The initial pipeline to load */ var initialPipelineName: String? = null @@ -409,11 +412,6 @@ class EOCVSim : KoinComponent { * Whether the specified pipeline must be searched in the CLASSPATH or from the workspace */ var initialPipelineSource: PipelineSource? = null - - /** - * An alternative path for the OpenCV native library to be loaded at runtime - */ - var opencvNativeLibrary: File? = null } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Lifecycle.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Lifecycle.kt index 0a2b3c59..e906a8d8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Lifecycle.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Lifecycle.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim sealed interface LifecycleSignal { @@ -5,4 +10,4 @@ sealed interface LifecycleSignal { enum class Reason { USER_REQUESTED, THREAD_EXIT, CRASH } } object Restart : LifecycleSignal -} \ No newline at end of file +} 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 9780a463..27f1d5fd 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 @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim import com.github.serivesmejia.eocvsim.pipeline.PipelineSource @@ -87,13 +69,6 @@ class EOCVSimCommandInterface : Runnable { @JvmField var initialPipelineSource = PipelineSource.CLASSPATH - @CommandLine.Option( - names = ["-o", "--opencvpath"], - description = ["Specifies an alternative path for the OpenCV native to be loaded at runtime"] - ) - @JvmField - var opencvNativePath: String? = null - override fun run() { val parameters = EOCVSim.Parameters() @@ -106,10 +81,6 @@ class EOCVSimCommandInterface : Runnable { parameters.initialPipelineSource = initialPipelineSource } - if (opencvNativePath != null) { - parameters.opencvNativeLibrary = checkPath("OpenCV Native", opencvNativePath!!, false) - } - GlobalContext.startKoin { modules( eocvSimModule, @@ -144,4 +115,4 @@ class EOCVSimCommandInterface : Runnable { return file } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt index d66a5de2..53bc1fd0 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim import com.github.serivesmejia.eocvsim.config.ConfigManager @@ -51,7 +56,8 @@ val eocvSimModule = module { single { Orchestrator(scope = get(), tasks = getAll(), name = "Main") } single(named("lifecycle")) { Channel(Channel.BUFFERED) } + single(named("onCrash")) { EventHandler("OnCrash") } single(named("onMainLoop")) { EventHandler("OnMainLoop") } } -private fun KoinDefinition.bindOrchestrable() = this bind Orchestrable::class \ No newline at end of file +private fun KoinDefinition.bindOrchestrable() = this bind Orchestrable::class 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 5b41416b..bb2d2fff 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 @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.config; import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel; @@ -71,4 +53,4 @@ public boolean getFlag(String flagName) { return flags.get(flagName) != null && flags.get(flagName); } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigLoader.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigLoader.java index 5f95550e..39ef2b5b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigLoader.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigLoader.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.config; import com.github.serivesmejia.eocvsim.util.SysUtil; @@ -77,3 +59,4 @@ public void saveToFile(Config conf) { } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt index ab74e8e8..871a1af5 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.config @@ -64,3 +46,4 @@ class ConfigManager : PhaseOrchestrableBase(), KoinComponent { configLoader.saveToFile(config) } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt index bb95e5e1..54d75257 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.gui @@ -31,6 +13,7 @@ import com.github.serivesmejia.eocvsim.gui.dialog.source.* import com.github.serivesmejia.eocvsim.input.SourceType import com.github.serivesmejia.eocvsim.plugin.output.PluginOutputHandler import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.util.exception.handling.CrashReport import io.github.deltacv.eocvsim.plugin.loader.PluginManager import kotlinx.coroutines.CoroutineScope import org.koin.core.component.KoinComponent @@ -44,11 +27,13 @@ import javax.swing.filechooser.FileNameExtensionFilter class DialogFactory : KoinComponent { - val visualizer: Visualizer by inject() - val pluginManager: PluginManager by inject() - val configManager: ConfigManager by inject() - val scope: CoroutineScope by inject() - val outputHandler: PluginOutputHandler by inject() + private val visualizer: Visualizer by inject() + private val pluginManager: PluginManager by inject() + private val configManager: ConfigManager by inject() + private val scope: CoroutineScope by inject() + private val outputHandler: PluginOutputHandler by inject() + + private val frame: JFrame? get() = if (visualizer.hasFinishedInitializing) visualizer.frame else null fun createYesOrNo(parent: Component?, message: String, submessage: String, result: (Int) -> Unit) { val panel = JPanel() @@ -180,8 +165,8 @@ class DialogFactory : KoinComponent { invokeLater { PluginOutput(outputHandler, pluginManager, configManager, scope) } } - fun createSplashScreen(closeHandler: EventHandler?) { - invokeLater { SplashScreen(closeHandler) } + fun createSplashScreen(vararg closeHandlers: EventHandler) { + invokeLater { SplashScreen(*closeHandlers) } } fun createIAmA() { @@ -196,8 +181,11 @@ class DialogFactory : KoinComponent { invokeLater { CreateWorkspace() } } - fun createCrashReport(crash: String?) { - invokeLater { CrashReportOutput(visualizer.frame, crash ?: "") } + fun instantiateCrashReport(crash: String?, headerText: String? = null) = + CrashReportOutput(frame, crash ?: "", headerText) + + fun createCrashReport(crash: String?, headerText: String? = null) { + invokeLater { instantiateCrashReport(crash, headerText) } } fun createFileAlreadyExistsDialog(): FileAlreadyExists.UserChoice { @@ -264,3 +252,4 @@ class DialogFactory : KoinComponent { } } } + 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 7efc4ccc..6fee1b37 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,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui object EOCVSimIconLibrary { @@ -61,4 +43,4 @@ object EOCVSimIconLibrary { 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/IconDelegate.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/IconDelegate.kt index 8f23937f..36effe94 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/IconDelegate.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/IconDelegate.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.gui @@ -35,4 +17,4 @@ class IconDelegate(val name: String, val path: String, allowInvert: Boolean = tr operator fun getValue(any: Any, property: KProperty<*>): Icons.NamedImageIcon { return Icons.getImage(name) } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Icons.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Icons.kt index f11c66e1..482645b1 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Icons.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Icons.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui import com.github.serivesmejia.eocvsim.gui.util.GuiUtil @@ -154,4 +136,4 @@ object Icons : KoinComponent { } } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt index 2c547c64..d5e130fd 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.gui @@ -479,3 +461,4 @@ class Visualizer : PhaseOrchestrableBase(), KoinComponent { } } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/EnumComboBox.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/EnumComboBox.kt index 3530403c..4b19682b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/EnumComboBox.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/EnumComboBox.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.input import com.github.serivesmejia.eocvsim.util.event.EventHandler @@ -76,3 +58,4 @@ class EnumComboBox> @JvmOverloads constructor( } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/FileSelector.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/FileSelector.kt index 922b01e7..f7cbf5b1 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/FileSelector.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/FileSelector.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.input import com.github.serivesmejia.eocvsim.gui.DialogFactory @@ -75,3 +57,4 @@ class FileSelector(columns: Int = 18, } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/SizeFields.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/SizeFields.kt index e63c99ce..26d59348 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/SizeFields.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/input/SizeFields.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.input import com.github.serivesmejia.eocvsim.EOCVSim @@ -138,4 +120,4 @@ class SizeFields(initialSize: Size = EOCVSim.DEFAULT_EOCV_SIZE, } } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/ColorPicker.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/ColorPicker.kt index 7826c415..408d5dd2 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/ColorPicker.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/ColorPicker.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.tuner import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary @@ -111,3 +93,4 @@ class ColorPicker(private val viewport: SwingOpenCvViewport) { } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.kt index ca22cf6e..4b828c25 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanel.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.tuner @@ -197,3 +202,4 @@ class TunableFieldPanel(val tunableField: TunableField<*>) : JPanel(), KoinCompo } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelConfig.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelConfig.kt index 92cbd747..eea8b933 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelConfig.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelConfig.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.tuner import com.github.serivesmejia.eocvsim.config.ConfigManager @@ -347,4 +329,4 @@ class TunableFieldPanelConfig(private val fieldOptions: TunableFieldPanelOptions return fields } -} \ No newline at end of file +} 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 92e8f908..125d8f52 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 @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.tuner import com.github.serivesmejia.eocvsim.gui.Visualizer @@ -206,4 +188,4 @@ class TunableFieldPanelOptions(val fieldPanel: TunableFieldPanel) : JPanel(), Ko repaint() } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableComboBox.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableComboBox.kt index a1d40a14..2c02a2c6 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableComboBox.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableComboBox.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.tuner.element import com.github.serivesmejia.eocvsim.pipeline.PipelineManager @@ -53,3 +58,4 @@ class TunableComboBox(val tunableValue: TunableEnum<*>) : JComboBox(), K } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableSlider.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableSlider.kt index 7b69df70..c8f5c539 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableSlider.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableSlider.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.tuner.element import com.github.serivesmejia.eocvsim.pipeline.PipelineManager @@ -76,4 +58,4 @@ class TunableSlider(val tunableValue: TunableNumber, } } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.kt index 2c856ef8..fc7c10ef 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/element/TunableTextField.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.tuner.element import com.github.serivesmejia.eocvsim.EOCVSim @@ -152,3 +157,4 @@ class TunableTextField(val tunableValue: TunableValue<*>) : JTextField(), KoinCo } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/CreateSourcePanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/CreateSourcePanel.kt index dda4761d..aa5ebbaa 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/CreateSourcePanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/CreateSourcePanel.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.visualizer import com.github.serivesmejia.eocvsim.EOCVSim @@ -66,4 +48,4 @@ class CreateSourcePanel(eocvSim: EOCVSim) : JPanel(GridLayout(2, 1)), KoinCompon add(nextPanel) } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/InputSourceDropTarget.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/InputSourceDropTarget.kt index 0df9cf6b..a1392e62 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/InputSourceDropTarget.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/InputSourceDropTarget.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.visualizer import com.github.serivesmejia.eocvsim.EOCVSim @@ -64,4 +46,4 @@ class InputSourceDropTarget : DropTarget(), KoinComponent { } } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/SidebarPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/SidebarPanel.kt index bbe04870..871d3476 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/SidebarPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/SidebarPanel.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.gui.component.visualizer @@ -108,4 +90,4 @@ class SidebarPanel : JTabbedPane(), KoinComponent { abstract fun onActivated() abstract fun onDeactivated() } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/TelemetryPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/TelemetryPanel.kt index f9436a14..38e94503 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/TelemetryPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/TelemetryPanel.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.visualizer import com.github.serivesmejia.eocvsim.pipeline.PipelineManager @@ -154,3 +136,4 @@ class TelemetryPanel : JPanel(), TelemetryTransmissionReceiver, KoinComponent { } } + 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 d90ca0a1..4cdbf59a 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 @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.visualizer import com.github.serivesmejia.eocvsim.LifecycleSignal @@ -259,3 +241,4 @@ class TopMenuBar : JMenuBar(), KoinComponent { } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeControlsPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeControlsPanel.kt index 411e4e0b..39378614 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeControlsPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeControlsPanel.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.visualizer.opmode import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary @@ -154,4 +136,4 @@ class OpModeControlsPanel : JPanel(), KoinComponent { currentOpMode?.requestOpModeStop() } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModePopupPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModePopupPanel.kt index f3bea7cb..2f525eae 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModePopupPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModePopupPanel.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.visualizer.opmode import javax.swing.JList @@ -41,4 +23,4 @@ class OpModePopupPanel(autonomousSelector: JList<*>) : JPanel() { autonomousSelector.selectionModel.clearSelection() } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeSelectorPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeSelectorPanel.kt index 13df6867..dbdffb07 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeSelectorPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeSelectorPanel.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.gui.component.visualizer.opmode @@ -381,4 +363,4 @@ class OpModeSelectorPanel(val opModeControlsPanel: OpModeControlsPanel) : JPanel opModeControlsPanel.stopCurrentOpMode() } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/SidebarOpModeTabPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/SidebarOpModeTabPanel.kt index 521870e9..05c73e5b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/SidebarOpModeTabPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/SidebarOpModeTabPanel.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.visualizer.opmode import com.github.serivesmejia.eocvsim.gui.component.visualizer.SidebarPanel @@ -68,4 +50,4 @@ class SidebarOpModeTabPanel : SidebarPanel.TabJPanel(), KoinComponent { opModeSelectorPanel.isActive = false opModeSelectorPanel.reset(-1) } -} \ No newline at end of file +} 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 948f8696..861980d3 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 @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline import com.github.serivesmejia.eocvsim.pipeline.PipelineManager @@ -153,4 +135,4 @@ class PipelineSelectorButtonsPanel : JPanel(GridBagLayout()), KoinComponent { }) } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/PipelineSelectorPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/PipelineSelectorPanel.kt index 5f60c252..b0ba2024 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/PipelineSelectorPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/PipelineSelectorPanel.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline import com.github.serivesmejia.eocvsim.gui.Visualizer @@ -202,3 +184,4 @@ class PipelineSelectorPanel : JPanel(), KoinComponent { } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SidebarPipelineTabPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SidebarPipelineTabPanel.kt index 4881da33..f2766bee 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SidebarPipelineTabPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SidebarPipelineTabPanel.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline import com.github.serivesmejia.eocvsim.pipeline.PipelineManager @@ -112,4 +94,4 @@ class SidebarPipelineTabPanel : SidebarPanel.TabJPanel(), KoinComponent { pipelineSelectorPanel.isActive = false } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt index df35df1c..c8be6a19 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/pipeline/SourceSelectorPanel.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline import com.github.serivesmejia.eocvsim.gui.DialogFactory @@ -229,3 +211,4 @@ class SourceSelectorPanel : JPanel(), KoinComponent { } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.kt index 5df638da..6f834036 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.gui.dialog @@ -159,3 +141,4 @@ class About : KoinComponent { about.isVisible = true } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.kt index ce90e8f3..5221e52f 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Configuration.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog import com.github.serivesmejia.eocvsim.LifecycleSignal @@ -209,4 +191,4 @@ class Configuration : KoinComponent { dialog.isVisible = false dialog.dispose() } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/CrashReportOutput.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/CrashReportOutput.kt index b477e1a8..3824e1b7 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/CrashReportOutput.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/CrashReportOutput.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog import com.formdev.flatlaf.intellijthemes.FlatArcDarkIJTheme @@ -39,14 +21,15 @@ import kotlin.system.exitProcess class CrashReportOutput( parent: JFrame?, - crashReport: String + crashReport: String, + headerText: String? = null ){ val output by lazy { JDialog(parent) } private val reportPanel by lazy { - OutputPanel(DefaultBottomButtonsPanel { exitProcess(0) }) + OutputPanel(DefaultBottomButtonsPanel(hasClearButton = false) { exitProcess(0) }) } init { @@ -60,7 +43,7 @@ class CrashReportOutput( reportPanel.resetScroll() } - output.add(JLabel("An unexpected fatal error occurred, please report this to the developers.").apply { + output.add(JLabel(headerText ?: "An unexpected fatal error occurred, please report this to the developers.").apply { font = font.deriveFont(Font.BOLD, 18f) alignmentX = JLabel.CENTER_ALIGNMENT }) @@ -80,4 +63,4 @@ class CrashReportOutput( output.setLocationRelativeTo(null) output.isVisible = true } -} \ No newline at end of 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 index 5b8449d7..fc07a147 100644 --- 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 @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary @@ -101,4 +106,4 @@ class CreateWorkspace : KoinComponent { dialog.isVisible = true } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/FileAlreadyExists.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/FileAlreadyExists.kt index c0eadb55..547f3c52 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/FileAlreadyExists.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/FileAlreadyExists.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.gui.dialog @@ -80,3 +62,4 @@ class FileAlreadyExists : KoinComponent { enum class UserChoice { NA, REPLACE, CANCEL } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Output.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Output.kt index b21b4d4f..cbfeda6f 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Output.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/Output.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog import com.github.serivesmejia.eocvsim.gui.Visualizer @@ -222,7 +204,7 @@ class Output @JvmOverloads constructor( class PipelineBottomButtonsPanel( closeCallback: () -> Unit - ) : OutputPanel.DefaultBottomButtonsPanel(closeCallback) { + ) : OutputPanel.DefaultBottomButtonsPanel(closeCallback = closeCallback) { val pauseButton = JToggleButton("Pause") override fun create(panel: OutputPanel) { @@ -234,7 +216,7 @@ class Output @JvmOverloads constructor( class BuildOutputBottomButtonsPanel( closeCallback: () -> Unit, - ) : OutputPanel.DefaultBottomButtonsPanel(closeCallback) { + ) : OutputPanel.DefaultBottomButtonsPanel(closeCallback = closeCallback) { val buildAgainButton = JButton("Build again") override fun create(panel: OutputPanel) { @@ -244,3 +226,4 @@ class Output @JvmOverloads constructor( } } } + 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 42c660cc..248cae4e 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 @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog import com.github.serivesmejia.eocvsim.LifecycleSignal @@ -477,4 +459,4 @@ class PluginOutput( add(Box.createRigidArea(Dimension(4, 0))) } } -} \ No newline at end of file +} 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 2ac13134..42bc9bec 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 @@ -1,40 +1,23 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary import com.github.serivesmejia.eocvsim.gui.Icons import com.github.serivesmejia.eocvsim.util.event.EventHandler +import com.github.serivesmejia.eocvsim.util.event.ParamEventHandler import java.awt.* import javax.swing.JDialog import javax.swing.JPanel import javax.swing.SwingUtilities -class SplashScreen(closeHandler: EventHandler? = null) : JDialog() { +class SplashScreen(vararg closeHandlers: EventHandler) : JDialog() { init { - closeHandler?.once { + EventHandler.batchOnce(*closeHandlers) { SwingUtilities.invokeLater { isVisible = false dispose() @@ -76,4 +59,4 @@ class SplashScreen(closeHandler: EventHandler? = null) : JDialog() { 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/component/OutputPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/component/OutputPanel.kt index a59eb110..134c2203 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/component/OutputPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/component/OutputPanel.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog.component import com.formdev.flatlaf.FlatLaf @@ -100,6 +82,7 @@ class OutputPanel( } open class DefaultBottomButtonsPanel( + val hasClearButton: Boolean = true, override val closeCallback: () -> Unit ) : BottomButtonsPanel() { val copyButton = JButton("Copy") @@ -119,10 +102,12 @@ class OutputPanel( add(copyButton) add(Box.createRigidArea(Dimension(4, 0))) - clearButton.addActionListener { panel.outputArea.text = "" } + if(hasClearButton) { + clearButton.addActionListener { panel.outputArea.text = "" } - add(clearButton) - add(Box.createRigidArea(Dimension(4, 0))) + add(clearButton) + add(Box.createRigidArea(Dimension(4, 0))) + } closeButton.addActionListener { closeCallback() } @@ -139,3 +124,4 @@ abstract class BottomButtonsPanel : JPanel() { abstract fun create(panel: OutputPanel) } + 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 index 6488df96..659980cf 100644 --- 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 @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog.iama import com.github.serivesmejia.eocvsim.gui.DialogFactory @@ -137,4 +119,4 @@ class IAmA : KoinComponent { 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 index 485557c4..0e0280a8 100644 --- 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 @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog.iama import com.github.serivesmejia.eocvsim.gui.Visualizer @@ -139,4 +121,4 @@ class IAmAFirstRobotics : KoinComponent { // 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 index 43f0c9e1..a84a9c0d 100644 --- 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 @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog.iama import com.github.serivesmejia.eocvsim.gui.Visualizer @@ -137,4 +119,4 @@ class IAmAGeneralPublic : KoinComponent { // 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 index 16ed03b3..5b7b1609 100644 --- 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 @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog.iama import com.github.serivesmejia.eocvsim.gui.DialogFactory @@ -195,4 +177,4 @@ class IAmAPaperVision( dialog.isVisible = true } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt index 2eec0da9..4d81217a 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog.source import com.github.serivesmejia.eocvsim.gui.Visualizer @@ -258,4 +263,4 @@ class CreateCameraSource : KoinComponent { private fun close() { dialog.dispose() } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateHttpSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateHttpSource.kt index d8eb07ab..ccb7b58c 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateHttpSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateHttpSource.kt @@ -1,25 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog.source import com.github.serivesmejia.eocvsim.EOCVSim @@ -146,4 +129,4 @@ class CreateHttpSource : KoinComponent { override fun removeUpdate(e: DocumentEvent?) = action() }) } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt index 2b46c743..7c4c7b8c 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt @@ -1,25 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog.source import com.github.serivesmejia.eocvsim.EOCVSim @@ -187,3 +170,4 @@ class CreateImageSource( !inputSourceManager.isNameInUse(nameTextField.text) } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateSourceEx.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateSourceEx.kt index eda991f8..a58e0673 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateSourceEx.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateSourceEx.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog.source import com.github.serivesmejia.eocvsim.gui.DialogFactory @@ -135,4 +117,4 @@ class CreateSourceEx : KoinComponent { dialog.isVisible = true } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt index 031e8a08..6346aea2 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt @@ -1,25 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog.source import com.github.serivesmejia.eocvsim.EOCVSim @@ -183,4 +166,4 @@ class CreateVideoSource( override fun removeUpdate(e: DocumentEvent?) = action() }) } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/theme/Theme.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/theme/Theme.java index 1b061492..3835e92f 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/theme/Theme.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/theme/Theme.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.theme; import com.formdev.flatlaf.*; @@ -57,4 +39,4 @@ public enum Theme { public void install() throws ClassNotFoundException, UnsupportedLookAndFeelException, InstantiationException, IllegalAccessException { installRunn.install(); } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/theme/ThemeInstaller.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/theme/ThemeInstaller.java index 0c0e9d82..96cfe574 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/theme/ThemeInstaller.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/theme/ThemeInstaller.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.theme; import javax.swing.*; @@ -28,3 +10,4 @@ public interface ThemeInstaller { void install() throws ClassNotFoundException, UnsupportedLookAndFeelException, InstantiationException, IllegalAccessException; } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/GuiUtil.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/GuiUtil.kt index a0e828c5..3a3b89b6 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/GuiUtil.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/GuiUtil.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.gui.util @@ -154,3 +136,4 @@ object GuiUtil { return listModel } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/ThreadedMatPoster.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/ThreadedMatPoster.java index 3c0e7327..5aeae12e 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/ThreadedMatPoster.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/ThreadedMatPoster.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.util; import com.github.serivesmejia.eocvsim.util.fps.FpsCounter; @@ -220,4 +202,4 @@ public void run() { } } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/WebcamDriver.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/WebcamDriver.kt index 7ea6591a..62c6b811 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/WebcamDriver.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/WebcamDriver.kt @@ -1,28 +1,10 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.util enum class WebcamDriver { OpenPnp, OpenIMAJ, OpenCV -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/icon/PipelineListIconRenderer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/icon/PipelineListIconRenderer.kt index 520fe0b6..21bb1fb6 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/icon/PipelineListIconRenderer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/icon/PipelineListIconRenderer.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.util.icon import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary @@ -64,3 +46,4 @@ class PipelineListIconRenderer( } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/icon/SourcesListIconRenderer.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/icon/SourcesListIconRenderer.java index 1b408eff..4b8e23c2 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/icon/SourcesListIconRenderer.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/icon/SourcesListIconRenderer.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.util.icon; import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary; @@ -73,3 +55,4 @@ public Component getListCellRendererComponent( } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt index e7fa18d8..b4c96713 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.input @@ -95,3 +77,4 @@ abstract class InputSource : Comparable, KoinComponent { } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt index 69a9f101..ca8eee35 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.input import com.github.serivesmejia.eocvsim.util.event.ParamEventHandler @@ -165,4 +170,4 @@ class InputSourceInitializer : KoinComponent { } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceLoader.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceLoader.kt index 63facd3c..33cce197 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceLoader.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceLoader.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.input @@ -167,3 +149,4 @@ class InputSourceLoader { } } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt index ea2d0068..b0148605 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.input @@ -314,3 +296,4 @@ class InputSourceManager : PhaseOrchestrableBase(), KoinComponent { return sourcesList } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/SourceType.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/SourceType.kt index e78e097d..b529c70c 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/SourceType.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/SourceType.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.input @@ -70,3 +52,4 @@ enum class SourceType(val stubInstance: InputSource?, val coolName: String) { } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt index 8444c5f5..0a1052f8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.input.source import com.fasterxml.jackson.annotation.JsonAutoDetect @@ -246,4 +251,4 @@ class CameraSource : InputSource, KoinComponent { "CameraSource($webcamName, port=$cameraPortIndex, exactPortMatch=$exactPortMatch, vid=$vendorId, pid=$productId, ${videoMode?.stringify()})" private fun VideoMode.stringify() = "${width}x${height}@${fps} $pixelFormat" -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt index 1d5adc8d..3fbed8c9 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2025 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.input.source @@ -27,13 +9,14 @@ import com.fasterxml.jackson.annotation.JsonAutoDetect import com.fasterxml.jackson.annotation.JsonProperty import com.github.serivesmejia.eocvsim.input.InputSource import com.github.serivesmejia.eocvsim.input.InputSourceInitializer - -import io.github.deltacv.visionloop.io.MjpegHttpReader +import com.github.serivesmejia.eocvsim.config.ConfigManager +import io.github.deltacv.common.util.loggerForThis +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject import org.opencv.core.Mat -import org.opencv.core.MatOfByte -import org.opencv.imgcodecs.Imgcodecs import org.opencv.imgproc.Imgproc -import org.slf4j.LoggerFactory +import org.wpilib.vision.camera.CvSink +import org.wpilib.vision.camera.HttpCamera import javax.swing.filechooser.FileFilter @JsonAutoDetect( @@ -43,106 +26,86 @@ import javax.swing.filechooser.FileFilter setterVisibility = JsonAutoDetect.Visibility.NONE, creatorVisibility = JsonAutoDetect.Visibility.NONE ) -class HttpSource @JvmOverloads constructor( - @JsonProperty @JvmField var url: String = "" -) : InputSource() { +class HttpSource @JvmOverloads constructor ( + @field:JsonProperty @JvmField var url: String = "" +) : InputSource(), KoinComponent { override val hasSlowInitialization: Boolean get() = true - @Transient private var mjpegHttpReader: MjpegHttpReader? = null - - @Transient private var buf: MatOfByte? = null - @Transient private var img: Mat? = null + @Transient private var camera: HttpCamera? = null - @Transient private var iterator: Iterator? = null + @Transient private var cvSink: CvSink? = null + @Transient private var lastFrame = Mat() + @Transient private var initialized = false @Transient private var capTimeNanos: Long = 0 - - @Transient private val logger = LoggerFactory.getLogger(javaClass) + private val configManager: ConfigManager by inject() + private val logger by loggerForThis() override fun init(): Boolean { - buf = MatOfByte() - img = Mat() - try { - mjpegHttpReader = MjpegHttpReader(url) - mjpegHttpReader!!.start() + camera = HttpCamera("eocvsim_http_source", url) } catch (e: Exception) { - logger.error("Error while initializing MjpegHttpReader", e) + logger.error("Error while initializing HTTP camera", e) return false } try { - iterator = mjpegHttpReader!!.iterator() + cvSink = CvSink("eocvsim_http_sink").also { + it.source = camera + } } catch (e: Exception) { - logger.error("Error while getting MjpegHttpReader iterator", e) + logger.error("Error while setting up HTTP camera sink", e) return false } - logger.info("HttpSource initialized") + val ok = cvSink!!.grabFrame(lastFrame, configManager.config.webcamOpenTimeoutSec) + if (ok == 0L || lastFrame.empty()) { + logger.error("Failed to grab frame from HTTP camera: ${cvSink!!.error}") + return false + } - return mjpegHttpReader != null && iterator != null + logger.info("HttpSource initialized") + initialized = true + return true } - @Transient private var frame: ByteArray? = null override fun update(): Mat? { - if (mjpegHttpReader == null || iterator == null) return null - - val result = InputSourceInitializer.runWithTimeout(name) { - - frame = iterator!!.next() - frame != null - } - - if (result != InputSourceInitializer.Result.SUCCESS || frame == null) { - return null - } + if (!initialized) return null - val frameData = frame!! + val ok = cvSink?.grabFrame(lastFrame, configManager.config.webcamNewFrameTimeoutSec) ?: 0L - if (!dataIsValidJPEG(frameData)) { - logger.error("Received data is not a valid JPEG image") + if (ok == 0L || lastFrame.empty()) { return null } - - buf!!.fromArray(*frameData) - - if (buf!!.empty()) { - return null - } - - val mat = Imgcodecs.imdecode(buf, Imgcodecs.IMREAD_COLOR) - Imgproc.cvtColor(mat, img, Imgproc.COLOR_BGR2RGBA) - - mat.release() - capTimeNanos = System.nanoTime() - - return img + Imgproc.cvtColor(lastFrame, lastFrame, Imgproc.COLOR_BGR2RGBA) + return lastFrame } override fun reset() { - mjpegHttpReader?.stop() - mjpegHttpReader = null + if (!initialized) return + cvSink?.close() + cvSink = null + camera?.close() + camera = null + lastFrame.release() + initialized = false } override fun close() { - reset() + cvSink?.close() + camera?.close() } override fun onPause() { - if (mjpegHttpReader != null) { - reset() - } + cvSink?.close() + camera?.close() } override fun onResume() { - InputSourceInitializer.runWithTimeout(this) { - - - init() - } + InputSourceInitializer.runWithTimeout(this) { init() } } override fun internalCloneSource(): InputSource = HttpSource(url) @@ -150,80 +113,8 @@ class HttpSource @JvmOverloads constructor( override val fileFilters: FileFilter? get() = null override val captureTimeNanos: Long get() = capTimeNanos - override fun toString(): String { return "HttpSource($url)" } - - companion object { - private fun dataIsValidJPEG(data: ByteArray?): Boolean { - if (data == null || data.size < 2) { - return false - } - - val totalBytes = getJPEGSize(data, data.size) - - if (totalBytes == -1) { - return false - } - - return (data[0] == 0xFF.toByte() && - data[1] == 0xD8.toByte() && - data[totalBytes - 2] == 0xFF.toByte() && - data[totalBytes - 1] == 0xD9.toByte()) - } - - private fun getJPEGSize(data: ByteArray?, maxLength: Int): Int { - if (data == null || maxLength < 4) { - return -1 // Invalid or too small to be a JPEG - } - - // Check for SOI marker - if (data[0] != 0xFF.toByte() || data[1] != 0xD8.toByte()) { - return -1 // Not a JPEG - } - - var pos = 2 // Start after SOI - - while (pos < maxLength - 2) { - // Look for the next marker (0xFF xx) - if (data[pos] == 0xFF.toByte()) { - val marker = data[pos + 1] - - // End of Image (EOI) found - if (marker == 0xD9.toByte()) { - return pos + 2 // JPEG size - } - - // Skip padding bytes (some JPEGs use 0xFF 0x00) - if (marker == 0x00.toByte()) { - pos++ - continue - } - - // Most markers have a 2-byte length field - if ((marker >= 0xC0.toByte() && marker <= 0xFE.toByte()) && marker != 0xD9.toByte()) { - if (pos + 3 >= maxLength) { - return -1 // Incomplete JPEG - } - - // Read segment length (big-endian) - val segmentLength = ((data[pos + 2].toInt() and 0xFF) shl 8) or (data[pos + 3].toInt() and 0xFF) - - if (segmentLength < 2 || pos + segmentLength >= maxLength) { - return -1 // Corrupt or incomplete JPEG - } - - pos += segmentLength // Move to next marker - } else { - pos++ // Skip unknown byte - } - } else { - pos++ // Continue searching - } - } - - return -1 // No valid JPEG end found - } - } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.kt index cc5e54c4..328d0c5f 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.input.source @@ -42,8 +24,8 @@ import javax.swing.filechooser.FileFilter creatorVisibility = JsonAutoDetect.Visibility.NONE ) class ImageSource @JvmOverloads constructor( - @JsonProperty @JvmField var imgPath: String = "", - @JsonProperty @JvmField var size: Size = Size() + @field:JsonProperty @JvmField var imgPath: String = "", + @field:JsonProperty @JvmField var size: Size = Size() ) : InputSource() { @@ -151,3 +133,4 @@ class ImageSource @JvmOverloads constructor( } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/NullSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/NullSource.kt index 8840528a..1e92349b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/NullSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/NullSource.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.input.source @@ -50,3 +32,4 @@ class NullSource : InputSource() { } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt index 1d320b37..5b3a4fc7 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.input.source @@ -45,9 +27,8 @@ import javax.swing.filechooser.FileFilter creatorVisibility = JsonAutoDetect.Visibility.NONE ) class VideoSource @JvmOverloads constructor( - @JsonProperty @JvmField var videoPath: String = "", - @JsonProperty @JvmField var size: Size = Size() - + @field:JsonProperty @JvmField var videoPath: String = "", + @field:JsonProperty @JvmField var size: Size = Size() ) : InputSource() { override val hasSlowInitialization: Boolean get() = true @@ -210,3 +191,4 @@ class VideoSource @JvmOverloads constructor( } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/RecordingManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/RecordingManager.kt index 565777a1..87f56ac8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/RecordingManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/RecordingManager.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.output @@ -98,3 +80,4 @@ class RecordingManager : KoinComponent { } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/VideoRecordingSession.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/VideoRecordingSession.kt index d972600f..fa7b7103 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/VideoRecordingSession.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/VideoRecordingSession.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.output import com.github.serivesmejia.eocvsim.gui.util.ThreadedMatPoster @@ -158,4 +140,4 @@ class VideoRecordingSession( } } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/DefaultPipeline.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/DefaultPipeline.java index 56046e24..6f598b7b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/DefaultPipeline.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/DefaultPipeline.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.pipeline; import android.graphics.Color; @@ -82,3 +64,4 @@ public void onDrawFrame(android.graphics.Canvas canvas, int onscreenWidth, int o } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt index e55bb092..84f45991 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.pipeline import com.github.serivesmejia.eocvsim.EOCVSim @@ -873,3 +855,4 @@ enum class PipelineFps(val fps: Int, val coolName: String) { data class PipelineData(val source: PipelineSource, val clazz: Class<*>, val hidden: Boolean) enum class PipelineSource { CLASSPATH, COMPILED_ON_RUNTIME, ANONYMOUS } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/CompiledPipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/CompiledPipelineManager.kt index eb5d9453..9962cd19 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/CompiledPipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/CompiledPipelineManager.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.pipeline.compiled import com.github.serivesmejia.eocvsim.Build @@ -293,3 +275,4 @@ class CompiledPipelineManager : PhaseOrchestrableBase(), KoinComponent { } private fun File.mkdirLazy() = apply { mkdir() } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineClassLoader.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineClassLoader.kt index ba49acc8..8f5dc01f 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineClassLoader.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineClassLoader.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.pipeline.compiled import com.github.serivesmejia.eocvsim.util.ClasspathScan @@ -81,3 +86,4 @@ class PipelineClassLoader(pipelinesJar: File) : ClassLoader() { val OpenCvPipeline.isFromRuntimeCompilation: Boolean get() = this::class.java.classLoader is PipelineClassLoader + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineCompiler.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineCompiler.kt index 837aef3e..bd390d69 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineCompiler.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineCompiler.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.pipeline.compiled import com.github.serivesmejia.eocvsim.util.* @@ -177,4 +159,4 @@ enum class PipelineCompileStatus { NO_SOURCE } -data class PipelineCompileResult(val status: PipelineCompileStatus, val message: String) \ No newline at end of file +data class PipelineCompileResult(val status: PipelineCompileStatus, val message: String) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineStandardFileManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineStandardFileManager.kt index 686f1ba6..c8a80405 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineStandardFileManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineStandardFileManager.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.pipeline.compiled import com.github.serivesmejia.eocvsim.util.SysUtil @@ -71,3 +53,4 @@ class PipelineStandardFileManager(delegate: StandardJavaFileManager) : Delegatin } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/handler/PipelineHandler.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/handler/PipelineHandler.kt index 81c33913..6307976a 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/handler/PipelineHandler.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/handler/PipelineHandler.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.pipeline.handler import com.github.serivesmejia.eocvsim.input.InputSource @@ -39,4 +21,4 @@ interface PipelineHandler { fun onChange(beforePipeline: OpenCvPipeline?, newPipeline: OpenCvPipeline, telemetry: Telemetry) -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/handler/SpecificPipelineHandler.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/handler/SpecificPipelineHandler.kt index a446e210..fa992695 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/handler/SpecificPipelineHandler.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/handler/SpecificPipelineHandler.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.pipeline.handler import org.firstinspires.ftc.robotcore.external.Telemetry @@ -48,4 +30,4 @@ abstract class SpecificPipelineHandler( } } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/processor/ProcessorInstantiator.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/processor/ProcessorInstantiator.kt index ca591cb4..9046eee7 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/processor/ProcessorInstantiator.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/processor/ProcessorInstantiator.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.pipeline.instantiator.processor @@ -52,4 +34,4 @@ object ProcessorInstantiator : PipelineInstantiator { override fun variableTunerTarget(pipeline: OpenCvPipeline): VisionProcessor = (pipeline as ProcessorPipeline).processor -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/processor/ProcessorPipeline.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/processor/ProcessorPipeline.java index f3b184f6..96fd54d3 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/processor/ProcessorPipeline.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/processor/ProcessorPipeline.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.pipeline.instantiator.processor; import android.graphics.Canvas; @@ -55,3 +37,4 @@ public void onDrawFrame(Canvas canvas, int onscreenWidth, int onscreenHeight, fl } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineExceptionTracker.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineExceptionTracker.kt index 67e359f9..1de1d21c 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineExceptionTracker.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineExceptionTracker.kt @@ -1,25 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.pipeline.util import com.github.serivesmejia.eocvsim.pipeline.PipelineData @@ -161,3 +144,4 @@ class PipelineExceptionTracker(private val pipelineManager: PipelineManager) { var millisThrown: Long) } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineSnapshot.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineSnapshot.kt index 78bb310d..4316e498 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineSnapshot.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineSnapshot.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.pipeline.util import io.github.deltacv.common.util.loggerForThis @@ -131,3 +113,4 @@ class PipelineSnapshot(val virtualReflectContext: VirtualReflectContext, filter: } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/ConfigApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/ConfigApiImpl.kt index 51e9e704..d1f142b1 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/ConfigApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/ConfigApiImpl.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.config.ConfigManager @@ -43,4 +25,4 @@ class ConfigApiImpl(owner: EOCVSimPlugin, val internalConfigManager: ConfigManag override fun disableApi() { internalConfigManager.saveToFile() } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/DialogFactoryApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/DialogFactoryApiImpl.kt index ae1db405..82cd6895 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/DialogFactoryApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/DialogFactoryApiImpl.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.gui.DialogFactory @@ -136,4 +118,4 @@ class DialogFactoryApiImpl(owner: EOCVSimPlugin, val internalVisualizer: Visuali } override fun disableApi() { } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt index cb7cf13e..43ec60cc 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.EOCVSim @@ -66,4 +48,4 @@ class EOCVSimApiImpl(owner: EOCVSimPlugin) : EOCVSimApi(owner), KoinComponent { override fun disableApi() { logger.info("API for {} says: \"ight, imma head out\"", ownerName) } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EventHandlerHookApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EventHandlerHookApiImpl.kt index d4e4c8fa..32cb1ecf 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EventHandlerHookApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EventHandlerHookApiImpl.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.util.event.EventHandler @@ -57,4 +39,4 @@ class EventHandlerHookApiImpl(owner: EOCVSimPlugin, val eventHandler: EventHandl eventHandler.removeListener(id) } } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt index 0053677a..9bf4b52b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.input.InputSourceManager @@ -92,4 +74,4 @@ class InputSourceManagerApiImpl(owner: EOCVSimPlugin, val internalInputSourceMan } override fun disableApi() { } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/PipelineManagerApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/PipelineManagerApiImpl.kt index 98fcf9f1..93b7f198 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/PipelineManagerApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/PipelineManagerApiImpl.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.pipeline.PipelineManager @@ -195,4 +177,4 @@ private fun PipelineManager.PauseReason.mapToApi() = when(this) { PipelineManager.PauseReason.NOT_PAUSED -> PipelineManagerApi.PipelinePauseReason.NOT_PAUSED PipelineManager.PauseReason.USER_REQUESTED -> PipelineManagerApi.PipelinePauseReason.USER_REQUESTED PipelineManager.PauseReason.IMAGE_ONE_ANALYSIS -> PipelineManagerApi.PipelinePauseReason.IMAGE_SINGLE_SHOT -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SimpleHookApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SimpleHookApiImpl.kt index ed1943bb..93ac1e2c 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SimpleHookApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SimpleHookApiImpl.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.plugin.api.impl import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin @@ -60,3 +42,4 @@ class SimpleHookApiImpl(owner: EOCVSimPlugin) : HookApi(owner) { persistentHooks.clear() } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SwingApisImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SwingApisImpl.kt index 4660bb23..952b48d0 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SwingApisImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SwingApisImpl.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.gui.DialogFactory @@ -137,4 +119,4 @@ class JFileChooserApiImpl(owner: EOCVSimPlugin, val internalFileChooser: DialogF } override fun disableApi() { } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VariableTunerApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VariableTunerApiImpl.kt index 469afa57..b44082fb 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VariableTunerApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VariableTunerApiImpl.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.tuner.TunableField @@ -58,4 +40,4 @@ class VariableTunerApiImpl(owner: EOCVSimPlugin, val internalTunerManager: Tuner } override fun disableApi() { } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerApiImpl.kt index 749b3021..f43b9d10 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerApiImpl.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.gui.Visualizer @@ -110,4 +92,4 @@ class VisualizerViewportApiImpl(owner: EOCVSimPlugin, val internalViewport: Swin } override fun disableApi() { } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerComponentsApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerComponentsApiImpl.kt index 3e368a53..4ba28306 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerComponentsApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerComponentsApiImpl.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.gui.component.visualizer.TelemetryPanel @@ -76,4 +81,4 @@ class TelemetryPanelApiImpl(owner: EOCVSimPlugin, val internalPanel: TelemetryPa override fun disableApi() { } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/VisualPluginOutputHandler.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/VisualPluginOutputHandler.kt index 3cccae69..d8023ab7 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/VisualPluginOutputHandler.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/VisualPluginOutputHandler.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.plugin.output import com.github.serivesmejia.eocvsim.util.event.ParamEventHandler @@ -122,4 +104,4 @@ class VisualPluginOutputHandler : PluginOutputHandler { continuationDeferred.complete(Unit) } } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.kt index e5a36e46..6085580d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.tuner @@ -103,3 +85,4 @@ abstract class TunableField( ONLY_NUMBERS, ONLY_NUMBERS_DECIMAL, TEXT } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldAcceptor.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldAcceptor.kt index 787e8b8f..c9022b22 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldAcceptor.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldAcceptor.kt @@ -1,28 +1,10 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.tuner interface TunableFieldAcceptor { fun accept(clazz: Class<*>): Boolean -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldRegistry.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldRegistry.kt index 7a4f8866..d5261c91 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldRegistry.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldRegistry.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.tuner @@ -94,3 +76,4 @@ object TunableFieldRegistry { acceptors.clear() } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableValue.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableValue.kt index 87de7a41..e7040e93 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableValue.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableValue.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.tuner @@ -91,3 +73,4 @@ class TunableEnum>(initialValue: T, val enumValues: Array, suppl } } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt index ec2aaa69..c8e1c303 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.tuner import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel @@ -108,3 +113,4 @@ class TunerManager : PhaseOrchestrableBase(), KoinComponent { } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/exception/CancelTunableFieldAddingException.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/exception/CancelTunableFieldAddingException.kt index e2ddb339..e309be20 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/exception/CancelTunableFieldAddingException.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/exception/CancelTunableFieldAddingException.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.tuner.exception -class CancelTunableFieldAddingException(message: String) : RuntimeException(message) \ No newline at end of file +class CancelTunableFieldAddingException(message: String) : RuntimeException(message) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.kt index da649174..7ed73161 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.tuner.field import com.github.serivesmejia.eocvsim.EOCVSim @@ -34,3 +39,4 @@ class BooleanField( override val value: Boolean get() = _value } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/EnumField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/EnumField.kt index c7a4a1db..ba451815 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/EnumField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/EnumField.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.tuner.field import com.github.serivesmejia.eocvsim.EOCVSim @@ -41,4 +46,4 @@ class EnumField(instance: Any, override fun accept(clazz: Class<*>) = clazz.isEnum } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt index c368dde5..6f6ef73e 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.tuner.field import com.github.serivesmejia.eocvsim.EOCVSim @@ -38,3 +43,4 @@ abstract class NumericField( override val value: T get() = _value } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.kt index 370a394a..be8d238e 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.tuner.field import com.github.serivesmejia.eocvsim.EOCVSim @@ -35,3 +40,4 @@ class StringField( override val value: String get() = _value } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.kt index b75099a8..9bf35898 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.tuner.field.cv import com.github.serivesmejia.eocvsim.EOCVSim @@ -42,3 +47,4 @@ class PointField( override val value: Point get() = point } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/RectField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/RectField.kt index 2acb8e59..01285491 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/RectField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/RectField.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.tuner.field.cv import com.github.serivesmejia.eocvsim.EOCVSim @@ -74,4 +56,4 @@ class RectField(instance: Any, reflectionField: VirtualField) : override val value: Rect get() = Rect(rect.toDoubleArray()) -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.kt index 412ac51a..fa7c0f9b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.tuner.field.cv import com.github.serivesmejia.eocvsim.EOCVSim @@ -43,3 +48,4 @@ class ScalarField( override val value: Scalar get() = scalar } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt index 7d0d8ab1..c060f2c8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.tuner.field.numeric import com.github.serivesmejia.eocvsim.EOCVSim @@ -16,4 +21,4 @@ class DoubleField( override fun accept(clazz: Class<*>) = clazz == Double::class.java || clazz == java.lang.Double::class.java } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt index ce622c58..3a1eb714 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.tuner.field.numeric import com.github.serivesmejia.eocvsim.EOCVSim @@ -18,3 +23,4 @@ class FloatField( clazz == Float::class.java || clazz == java.lang.Float::class.java } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt index 9b03f7d8..37ea5e57 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.tuner.field.numeric import com.github.serivesmejia.eocvsim.EOCVSim @@ -18,3 +23,4 @@ class IntegerField( clazz == Int::class.java || clazz == java.lang.Integer::class.java } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt index 4e831c44..5e2b91c5 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.tuner.field.numeric import com.github.serivesmejia.eocvsim.EOCVSim @@ -18,3 +23,4 @@ class LongField( clazz == Long::class.java || clazz == java.lang.Long::class.java } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt index 222bfa5a..636b1c98 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package com.github.serivesmejia.eocvsim.util @@ -193,4 +175,4 @@ open class ClasspathScan { */ data class ScanResult( val pipelineClasses: List> -) \ No newline at end of file +) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/CombinedRuntimeLoader.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/CombinedRuntimeLoader.java index 122f109b..75eceeb0 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/CombinedRuntimeLoader.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/CombinedRuntimeLoader.java @@ -127,7 +127,7 @@ private static String getLoadErrorMessage(String libraryName, UnsatisfiedLinkErr if (System.getProperty("os.name").startsWith("Windows")) { msg.append( "A common cause of this error is missing the C++ runtime.\n" - + "Download the latest at https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads\n"); + + "Download the latest at https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170\n"); } return msg.toString(); } @@ -302,4 +302,4 @@ public static void loadLibraries(Class clazz, String... librariesToLoad) } } } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/FileFilters.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/FileFilters.kt index 05f0d8d0..5e518e8d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/FileFilters.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/FileFilters.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util import javax.swing.filechooser.FileNameExtensionFilter @@ -52,4 +34,4 @@ object FileFilters { */ @JvmField val logFileFilter = FileNameExtensionFilter("Log File (*.log)", "log") -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/LibraryLoader.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/LibraryLoader.java index 97e7cf4c..4943504b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/LibraryLoader.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/LibraryLoader.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util; import java.io.IOException; @@ -9,7 +14,7 @@ import org.wpilib.vision.camera.OpenCvLoader; public class LibraryLoader { - public record Result(boolean success, IOException error) {} + public record Result(boolean success, Throwable error) {} public static Result loadLibraries() { WPIUtilJNI.Helper.setExtractOnStaticLoad(false); @@ -27,7 +32,7 @@ public static Result loadLibraries() { "cscorejni"); CombinedRuntimeLoader.loadLibraries(LibraryLoader.class, Core.NATIVE_LIBRARY_NAME); - } catch(IOException e) { + } catch(IOException | UnsatisfiedLinkError e) { return new Result(false, e); } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/compiler/CompilerProvider.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/compiler/CompilerProvider.kt index 6f8c2bd1..0ed23961 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/compiler/CompilerProvider.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/compiler/CompilerProvider.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.compiler import io.github.deltacv.common.util.loggerOf @@ -50,4 +32,4 @@ val compiler by lazy { } } -data class Compiler(val name: String, val javaCompiler: JavaCompiler) \ No newline at end of file +data class Compiler(val name: String, val javaCompiler: JavaCompiler) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/compiler/DelegatingStandardFileManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/compiler/DelegatingStandardFileManager.kt index 28787dfe..6858a6f4 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/compiler/DelegatingStandardFileManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/compiler/DelegatingStandardFileManager.kt @@ -1,28 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives & (c) 2017 Robert Atkinson - * - * Based from the FTC SDK's org.firstinspires.ftc.onbotjava.OnBotJavaDelegatingStandardFileManager - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.compiler import java.io.File @@ -54,4 +34,4 @@ open class DelegatingStandardFileManager( override fun getLocation(location: JavaFileManager.Location): MutableIterable = delegate.getLocation(location) ?: Collections.emptyList() -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/compiler/JarPacker.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/compiler/JarPacker.kt index 1cd9616a..f0b13ccc 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/compiler/JarPacker.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/compiler/JarPacker.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.compiler import com.github.serivesmejia.eocvsim.util.SysUtil @@ -76,4 +58,4 @@ object JarPacker { jar.closeEntry() } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/MaxActiveContextsException.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/MaxActiveContextsException.kt index b725aff0..27418f5c 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/MaxActiveContextsException.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/MaxActiveContextsException.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.exception /** @@ -6,4 +11,4 @@ package com.github.serivesmejia.eocvsim.util.exception * from user code (like running a pipeline) in a time-expiring way. * @param message the message of the exception */ -class MaxActiveContextsException(message: String = "") : Exception(message) \ No newline at end of file +class MaxActiveContextsException(message: String = "") : Exception(message) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt index 193b8c10..90ca50eb 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.exception.handling import com.github.serivesmejia.eocvsim.EOCVSim @@ -96,9 +78,14 @@ class CrashReport(causedByException: Throwable, isDummy: Boolean = false) { private val sb = StringBuilder() init { - sb.appendLine("/--------------------------------\\").appendLine() - sb.appendLine(" EOCV-Sim v${EOCVSim.VERSION} crash report").appendLine() - sb.appendLine("\\--------------------------------/").appendLine() + val title = " EOCV-Sim v${EOCVSim.VERSION} crash report" + val dashes = "-".repeat(title.length) + val topBorder = "/$dashes\\" + val bottomBorder = "\\$dashes/" + + sb.appendLine(topBorder).appendLine() + sb.appendLine(title).appendLine() + sb.appendLine(bottomBorder).appendLine() sb.appendLine("! ${wittyComments.random()}").appendLine() @@ -109,23 +96,39 @@ class CrashReport(causedByException: Throwable, isDummy: Boolean = false) { sb.appendLine(causedByException.message).appendLine() } - sb.appendLine("==========================================").appendLine() + val infoSectionLines = listOf( + ": EOCV-Sim info", + " Version: ${EOCVSim.VERSION}", + " Built on: ${Build.buildDate}", + ": System specs", + " OS name: $OS_NAME", + " OS version: $OS_VERSION", + " Detected OS: $SYSUTIL_DETECTED_OS", + " Arch: $OS_ARCH", + " Detected Arch: ${SysUtil.ARCH}", + " Java version: $JAVA_VERSION", + " Java vendor: $JAVA_VENDOR", + " Last memory usage: ${SysUtil.getMemoryUsageMB()} MB" + ) + val infoSectionDivider = "=".repeat(infoSectionLines.maxOf { it.length }) + + sb.appendLine(infoSectionDivider).appendLine() - sb.appendLine(": EOCV-Sim info") - sb.appendLine(" Version: ${EOCVSim.VERSION}") - sb.appendLine(" Built on: ${Build.buildDate}").appendLine() + sb.appendLine(infoSectionLines[0]) + sb.appendLine(infoSectionLines[1]) + sb.appendLine(infoSectionLines[2]).appendLine() - sb.appendLine(": System specs") - sb.appendLine(" OS name: $OS_NAME") - sb.appendLine(" OS version: $OS_VERSION") - sb.appendLine(" Detected OS: $SYSUTIL_DETECTED_OS") - sb.appendLine(" Arch: $OS_ARCH") - sb.appendLine(" Detected Arch: ${SysUtil.ARCH}") - sb.appendLine(" Java version: $JAVA_VERSION") - sb.appendLine(" Java vendor: $JAVA_VENDOR") - sb.appendLine(" Last memory usage: ${SysUtil.getMemoryUsageMB()} MB").appendLine() + sb.appendLine(infoSectionLines[3]) + sb.appendLine(infoSectionLines[4]) + sb.appendLine(infoSectionLines[5]) + sb.appendLine(infoSectionLines[6]) + sb.appendLine(infoSectionLines[7]) + sb.appendLine(infoSectionLines[8]) + sb.appendLine(infoSectionLines[9]) + sb.appendLine(infoSectionLines[10]) + sb.appendLine(infoSectionLines[11]).appendLine() - sb.appendLine("==========================================").appendLine() + sb.appendLine(infoSectionDivider).appendLine() sb.appendLine(": Full thread dump").appendLine() @@ -138,7 +141,7 @@ class CrashReport(causedByException: Throwable, isDummy: Boolean = false) { } sb.appendLine() - sb.appendLine("==================================").appendLine() + sb.appendLine(infoSectionDivider).appendLine() sb.appendLine(": Full log").appendLine() @@ -193,3 +196,4 @@ class CrashReport(causedByException: Throwable, isDummy: Boolean = false) { override fun toString() = sb.toString() } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReportOutputMain.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReportOutputMain.kt index 0317014c..48209679 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReportOutputMain.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReportOutputMain.kt @@ -1,13 +1,25 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.exception.handling import com.formdev.flatlaf.intellijthemes.FlatArcDarkIJTheme import com.github.serivesmejia.eocvsim.Build import com.github.serivesmejia.eocvsim.gui.DialogFactory +import com.github.serivesmejia.eocvsim.gui.Visualizer +import com.github.serivesmejia.eocvsim.gui.dialog.CrashReportOutput import com.github.serivesmejia.eocvsim.util.SysUtil -import kotlinx.coroutines.Runnable +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import org.koin.core.context.GlobalContext +import org.koin.dsl.module import picocli.CommandLine import java.io.File import javax.swing.SwingUtilities +import kotlin.system.exitProcess object CrashReportOutputMain { @CommandLine.Command(name = "report", mixinStandardHelpOptions = true, version = [Build.versionString]) @@ -16,14 +28,33 @@ object CrashReportOutputMain { @JvmField var crashReportPath: String? = null override fun run() { + if (crashReportPath == null || crashReportPath!!.isEmpty()) { + System.err.println("Crash report path is required (-p or --path)") + exitProcess(1) + } + + val file = File(crashReportPath!!) + if (!file.exists()) { + System.err.println("Crash report file not found: $crashReportPath") + exitProcess(1) + } + SwingUtilities.invokeLater(FlatArcDarkIJTheme::setup) - // TODO: Make this actually work, need to setup koin - // dialogFactory.createCrashReport(null, SysUtil.loadFileStr(File(crashReportPath ?: ""))) + + try { + val crashContent = SysUtil.loadFileStr(file) + CrashReportOutput(null, crashContent ?: "") + } catch (e: Exception) { + System.err.println("Failed to display crash report: $ {e.message}") + e.printStackTrace() + exitProcess(1) + } } } @JvmStatic fun main(args: Array) { - CommandLine(CrashReportOutputCommandInterface()).execute(*args) + val exitCode = CommandLine(CrashReportOutputCommandInterface()).execute(*args) + exitProcess(exitCode) } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/EOCVSimUncaughtExceptionHandler.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/EOCVSimUncaughtExceptionHandler.kt index e1a74b3b..dba1f503 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/EOCVSimUncaughtExceptionHandler.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/EOCVSimUncaughtExceptionHandler.kt @@ -1,13 +1,27 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.exception.handling import com.github.serivesmejia.eocvsim.currentMainThread +import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.util.JavaProcess +import com.github.serivesmejia.eocvsim.util.event.EventHandler import io.github.deltacv.common.util.loggerForThis +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.swing.Swing +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import org.koin.core.qualifier.named import java.lang.Thread.sleep import javax.swing.SwingUtilities import kotlin.system.exitProcess -class EOCVSimUncaughtExceptionHandler private constructor() : Thread.UncaughtExceptionHandler { +class EOCVSimUncaughtExceptionHandler private constructor() : Thread.UncaughtExceptionHandler, KoinComponent{ companion object { const val MAX_UNCAUGHT_EXCEPTIONS_BEFORE_CRASH = 3 @@ -19,6 +33,9 @@ class EOCVSimUncaughtExceptionHandler private constructor() : Thread.UncaughtExc val logger by loggerForThis() + private val dialogFactory: DialogFactory by inject() + private val onCrash: EventHandler by inject(named("onCrash")) + private var uncaughtExceptionsCount = 0 override fun uncaughtException(t: Thread, e: Throwable) { @@ -34,20 +51,20 @@ class EOCVSimUncaughtExceptionHandler private constructor() : Thread.UncaughtExc logger.error("Uncaught exception thrown in \"${t.name}\" thread", e) // Exit if uncaught exception happened in the main thread - // since we would be basically in a deadlock state if that happened - // or if we have a lotta uncaught exceptions. + // since we would be basically in an invalid state if that happened if(t == currentMainThread || SwingUtilities.isEventDispatchThread() || e !is Exception || uncaughtExceptionsCount > MAX_UNCAUGHT_EXCEPTIONS_BEFORE_CRASH) { val file = CrashReport(e).saveCrashReport() logger.warn("If this error persists, open an issue on EOCV-Sim's GitHub attaching the crash report file.") logger.warn("The application will exit now (exit code 1)") - Thread { - JavaProcess.killSubprocessesOnExit = false - JavaProcess.exec(CrashReportOutputMain::class.java, listOf(), listOf("-p=${file.absolutePath}")) - }.start() + onCrash.run() - sleep(3000) // wait enough for the subprocess to start + runBlocking { + launch(Dispatchers.Swing) { + dialogFactory.instantiateCrashReport(file.readText()) + } + } exitProcess(1) } else { @@ -61,4 +78,4 @@ class EOCVSimUncaughtExceptionHandler private constructor() : Thread.UncaughtExc } } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/io/FileWatcher.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/io/FileWatcher.kt index d2a711ea..51e33e17 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/io/FileWatcher.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/io/FileWatcher.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.io import com.github.serivesmejia.eocvsim.util.event.EventHandler @@ -170,4 +175,4 @@ class FileWatcher( return name.substring(dot + 1).lowercase() in set } } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/JacksonJsonSupport.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/JacksonJsonSupport.kt index 60417efe..ef6e64e2 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/JacksonJsonSupport.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/JacksonJsonSupport.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.serialization import com.fasterxml.jackson.annotation.JsonAutoDetect @@ -55,4 +37,4 @@ object JacksonJsonSupport { .setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .enable(SerializationFeature.INDENT_OUTPUT) -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/VideoModeJackson.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/VideoModeJackson.kt index 8231892b..c4bc7092 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/VideoModeJackson.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/serialization/VideoModeJackson.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.util.serialization import com.fasterxml.jackson.core.JsonGenerator @@ -73,3 +78,4 @@ object VideoModeJackson { } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt index a95ef257..81e5af99 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.workspace import com.github.serivesmejia.eocvsim.EOCVSim @@ -307,3 +289,4 @@ class WorkspaceManager : PhaseOrchestrableBase(), KoinComponent { } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/config/WorkspaceConfig.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/config/WorkspaceConfig.java index 332b4a9a..b227cf91 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/config/WorkspaceConfig.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/config/WorkspaceConfig.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.workspace.config; import java.util.ArrayList; @@ -40,4 +22,4 @@ public class WorkspaceConfig { public String eocvSimVersion = ""; -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/config/WorkspaceConfigLoader.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/config/WorkspaceConfigLoader.kt index dca84e29..f8487a06 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/config/WorkspaceConfigLoader.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/config/WorkspaceConfigLoader.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.workspace.config import com.github.serivesmejia.eocvsim.Build @@ -47,4 +52,4 @@ class WorkspaceConfigLoader(var workspaceFile: File) { SysUtil.saveFileStr(workspaceConfigFile, configStr) } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/VSCodeLauncher.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/VSCodeLauncher.kt index bd830fab..667c4b91 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/VSCodeLauncher.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/VSCodeLauncher.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.workspace.util import kotlinx.coroutines.Dispatchers @@ -66,4 +48,4 @@ object VSCodeLauncher { @OptIn(DelicateCoroutinesApi::class) fun asyncLaunch(workspace: File, scope: CoroutineScope) = scope.launch(Dispatchers.IO) { launch(workspace) } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/WorkspaceTemplate.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/WorkspaceTemplate.kt index 6420d5a5..30ff2eea 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/WorkspaceTemplate.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/WorkspaceTemplate.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.workspace.util import io.github.deltacv.common.util.loggerForThis @@ -45,4 +27,4 @@ abstract class WorkspaceTemplate { abstract fun extractTo(folder: File): Boolean -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/DefaultWorkspaceTemplate.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/DefaultWorkspaceTemplate.kt index d47f59bd..b111afd8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/DefaultWorkspaceTemplate.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/DefaultWorkspaceTemplate.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.workspace.util.template import com.github.serivesmejia.eocvsim.util.SysUtil @@ -57,3 +39,4 @@ object DefaultWorkspaceTemplate : WorkspaceTemplate() { } } + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/GradleWorkspaceTemplate.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/GradleWorkspaceTemplate.kt index 096c5dc6..4b074437 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/GradleWorkspaceTemplate.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/GradleWorkspaceTemplate.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.workspace.util.template import com.github.serivesmejia.eocvsim.Build @@ -78,3 +60,4 @@ object GradleWorkspaceTemplate : WorkspaceTemplate() { } } + diff --git a/EOCV-Sim/src/main/java/com/qualcomm/robotcore/eventloop/opmode/OpModePipelineHandler.kt b/EOCV-Sim/src/main/java/com/qualcomm/robotcore/eventloop/opmode/OpModePipelineHandler.kt index f2fdcdca..8dddfae1 100644 --- a/EOCV-Sim/src/main/java/com/qualcomm/robotcore/eventloop/opmode/OpModePipelineHandler.kt +++ b/EOCV-Sim/src/main/java/com/qualcomm/robotcore/eventloop/opmode/OpModePipelineHandler.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.qualcomm.robotcore.eventloop.opmode import com.github.serivesmejia.eocvsim.input.InputSource @@ -118,4 +123,4 @@ val OpMode.autonomousAnnotation get() = this.javaClass.autonomousAnnotation /** * Extension function to get the TeleOp annotation of an OpMode */ -val OpMode.teleopAnnotation get() = this.javaClass.teleopAnnotation \ No newline at end of file +val OpMode.teleopAnnotation get() = this.javaClass.teleopAnnotation 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 ec563cb6..bec48029 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 @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.gui.dialog import com.formdev.flatlaf.FlatLightLaf @@ -187,4 +169,4 @@ private fun wrapText(text: String, maxLineLength: Int): String { // Join all lines with
for HTML return wrappedLines.joinToString(breakTag) -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSource.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSource.kt index 751306d4..af142d3e 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSource.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSource.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.input import com.github.serivesmejia.eocvsim.input.InputSource @@ -53,4 +58,4 @@ class VisionInputSource( } } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt index b6f16c30..821d6174 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.input import com.github.serivesmejia.eocvsim.input.InputSourceManager @@ -98,4 +103,4 @@ class VisionInputSourceProvider( } override fun viewport() = viewport -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceControlMap.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceControlMap.kt index 27148794..5a215fcf 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceControlMap.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceControlMap.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.input.control import com.github.serivesmejia.eocvsim.input.source.CameraSource @@ -16,4 +21,4 @@ class CameraSourceControlMap( else -> null } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt index 0226d295..9dec5a81 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.input.control import com.github.serivesmejia.eocvsim.input.source.CameraSource @@ -151,4 +156,4 @@ class CameraSourceExposureControl( ): Long { return TimeUnit.MILLISECONDS.convert(duration, unit) } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/pipeline/StreamableOpenCvPipelineInstantiator.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/pipeline/StreamableOpenCvPipelineInstantiator.kt index e5cffaa5..6d00d0b1 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/pipeline/StreamableOpenCvPipelineInstantiator.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/pipeline/StreamableOpenCvPipelineInstantiator.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.pipeline import com.github.serivesmejia.eocvsim.pipeline.instantiator.DefaultPipelineInstantiator @@ -22,4 +27,4 @@ class StreamableOpenCvPipelineInstantiator( override fun variableTunerTarget(pipeline: OpenCvPipeline) = pipeline -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/EmbeddedPluginLoader.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/EmbeddedPluginLoader.kt index 313eae69..2fd93bde 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/EmbeddedPluginLoader.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/EmbeddedPluginLoader.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.loader import com.github.serivesmejia.eocvsim.config.ConfigLoader @@ -165,3 +147,4 @@ class EmbeddedPluginLoader( return hasSuperAccess } } + diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt index 7a940e83..43f234d2 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.loader import com.github.serivesmejia.eocvsim.EOCVSim @@ -293,4 +275,4 @@ class EmbeddedFilePluginLoader( init { fetchInfoFromToml() } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginClassLoader.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginClassLoader.kt index a0b8e482..7339e020 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginClassLoader.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginClassLoader.kt @@ -1,24 +1,6 @@ /* * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package io.github.deltacv.eocvsim.plugin.loader @@ -36,6 +18,7 @@ import java.lang.ref.WeakReference import java.io.File import java.io.IOException import java.io.InputStream +import java.net.URI import java.net.URL import java.util.zip.ZipEntry import java.util.zip.ZipFile @@ -223,7 +206,7 @@ class PluginClassLoader( } try { - return URL("jar:file:${file.absolutePath}!/$name") + return URI("jar:file:${file.absolutePath}!/$name").toURL() } catch (_: Exception) { } } catch (_: Exception) { @@ -321,4 +304,4 @@ class PluginClassLoader( override fun toString() = "PluginClassLoader@\"${pluginJar.name}\"" override val pluginContext by lazy { PluginContext(pluginLoader) } -} \ No newline at end of file +} 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 b6608d71..85026719 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 @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.loader import com.github.serivesmejia.eocvsim.Build @@ -380,4 +362,4 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { configManager.config.flags[loader.hash()] = enabled configManager.saveToFile() } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt index 7cb82853..6b892cb5 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.repository import com.github.serivesmejia.eocvsim.plugin.output.PluginDialogSignal @@ -391,4 +373,4 @@ class PluginRepositoryManager( deps.joinToString(File.pathSeparator).replace("\\", "/").trimEnd(File.pathSeparatorChar).hashString } -class InvalidFileException(msg: String) : RuntimeException(msg) \ No newline at end of file +class InvalidFileException(msg: String) : RuntimeException(msg) diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryUpdateChecker.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryUpdateChecker.kt index ca76a702..edbc57ce 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryUpdateChecker.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryUpdateChecker.kt @@ -1,30 +1,13 @@ /* * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package io.github.deltacv.eocvsim.plugin.repository import io.github.deltacv.common.util.loggerOf import io.github.deltacv.common.util.ParsedVersion +import java.net.URI import java.net.URL import javax.xml.parsers.DocumentBuilderFactory @@ -67,7 +50,7 @@ fun findArtifactLatest(repositoryUrl: String, artifactId: String): ParsedVersion return try { // Fetch and parse the maven-metadata.xml - val metadataXml = URL(mavenMetadataUrl).readText() + val metadataXml = URI(mavenMetadataUrl).toURL().readText() val factory = DocumentBuilderFactory.newInstance() val builder = factory.newDocumentBuilder() @@ -102,4 +85,4 @@ fun checkForUpdates( } return null -} \ 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 523f6104..fb33b756 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 @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.security.superaccess import com.github.serivesmejia.eocvsim.util.extension.fileHash @@ -252,4 +234,4 @@ object SuperAccessDaemon { return null } -} \ No newline at end of file +} 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 178948a6..26ba50dc 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 @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.plugin.security.superaccess import com.github.serivesmejia.eocvsim.util.JavaProcess @@ -251,4 +233,4 @@ class SuperAccessDaemonClient( } } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/MethodCallByteCodeChecker.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/MethodCallByteCodeChecker.kt index 22c881bd..e634417a 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/MethodCallByteCodeChecker.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/MethodCallByteCodeChecker.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.sandbox.restrictions import org.objectweb.asm.ClassReader @@ -85,4 +67,4 @@ class MethodCallByteCodeChecker( } } -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/EOCVSimTelemetryImpl.java b/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/EOCVSimTelemetryImpl.java index 1af5a2f2..5def86b0 100644 --- a/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/EOCVSimTelemetryImpl.java +++ b/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/EOCVSimTelemetryImpl.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson @@ -1013,3 +1018,4 @@ protected void onAddData() // no-op } } + diff --git a/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/TelemetryInternal.java b/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/TelemetryInternal.java index 4abbe605..e927e9f9 100644 --- a/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/TelemetryInternal.java +++ b/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/TelemetryInternal.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2016 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2016 Robert Atkinson All rights reserved. @@ -33,4 +38,4 @@ public interface TelemetryInternal { boolean tryUpdateIfDirty(); void resetTelemetryForOpMode(); -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/TelemetryTransmissionReceiver.kt b/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/TelemetryTransmissionReceiver.kt index bbf2c20c..e281d862 100644 --- a/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/TelemetryTransmissionReceiver.kt +++ b/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/TelemetryTransmissionReceiver.kt @@ -1,7 +1,12 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package org.firstinspires.ftc.robotcore.internal.opmode import org.firstinspires.ftc.robotcore.external.Telemetry interface TelemetryTransmissionReceiver { fun onTelemetryTransmission(text: String, srcTelemetry: Telemetry) -} \ No newline at end of file +} diff --git a/EOCV-Sim/src/main/java/org/openftc/easyopencv/ProcessFrameInternalAccessor.kt b/EOCV-Sim/src/main/java/org/openftc/easyopencv/ProcessFrameInternalAccessor.kt index 331a5afa..9ef1d253 100644 --- a/EOCV-Sim/src/main/java/org/openftc/easyopencv/ProcessFrameInternalAccessor.kt +++ b/EOCV-Sim/src/main/java/org/openftc/easyopencv/ProcessFrameInternalAccessor.kt @@ -1,28 +1,10 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv import org.opencv.core.Mat -fun OpenCvPipeline.processFrameInternal(frame: Mat): Mat? = processFrameInternal(frame) \ No newline at end of file +fun OpenCvPipeline.processFrameInternal(frame: Mat): Mat? = processFrameInternal(frame) diff --git a/EOCV-Sim/src/main/java/org/openftc/easyopencv/TimestampedPipelineHandler.kt b/EOCV-Sim/src/main/java/org/openftc/easyopencv/TimestampedPipelineHandler.kt index afae8a04..8654f491 100644 --- a/EOCV-Sim/src/main/java/org/openftc/easyopencv/TimestampedPipelineHandler.kt +++ b/EOCV-Sim/src/main/java/org/openftc/easyopencv/TimestampedPipelineHandler.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv import com.github.serivesmejia.eocvsim.input.InputSource @@ -44,4 +26,4 @@ class TimestampedPipelineHandler : SpecificPipelineHandler T get(Class classType, String deviceName) { throw new IllegalArgumentException("Unknown device type " + classType.getName()); } -} \ No newline at end of file +} diff --git a/Vision/src/main/java/io/github/deltacv/eocvsim/pipeline/StreamableOpenCvPipeline.java b/Vision/src/main/java/io/github/deltacv/eocvsim/pipeline/StreamableOpenCvPipeline.java index 8e385641..49c9c22c 100644 --- a/Vision/src/main/java/io/github/deltacv/eocvsim/pipeline/StreamableOpenCvPipeline.java +++ b/Vision/src/main/java/io/github/deltacv/eocvsim/pipeline/StreamableOpenCvPipeline.java @@ -1,24 +1,6 @@ /* * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package io.github.deltacv.eocvsim.pipeline; @@ -67,4 +49,4 @@ public ImageStreamer getStreamer() { } } -} \ No newline at end of file +} diff --git a/Vision/src/main/java/io/github/deltacv/eocvsim/stream/ImageStreamer.kt b/Vision/src/main/java/io/github/deltacv/eocvsim/stream/ImageStreamer.kt index 74fd764a..dfa60b46 100644 --- a/Vision/src/main/java/io/github/deltacv/eocvsim/stream/ImageStreamer.kt +++ b/Vision/src/main/java/io/github/deltacv/eocvsim/stream/ImageStreamer.kt @@ -1,25 +1,6 @@ - /* * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package io.github.deltacv.eocvsim.stream @@ -28,4 +9,4 @@ import org.opencv.core.Mat interface ImageStreamer { fun sendFrame(id: Int, image: Mat, cvtCode: Int? = null) -} \ No newline at end of file +} diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/FrameReceiverOpenCvCamera.java b/Vision/src/main/java/io/github/deltacv/vision/external/FrameReceiverOpenCvCamera.java index a449dd74..963e9db3 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/FrameReceiverOpenCvCamera.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/FrameReceiverOpenCvCamera.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2023 OpenFTC & EOCV-Sim implementation by Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external; import io.github.deltacv.vision.external.source.VisionSource; @@ -226,4 +208,4 @@ public void consume(Mat frame, long timestamp) { public void stop() { closeCameraDevice(); } -} \ No newline at end of file +} diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/PipelineRenderHook.kt b/Vision/src/main/java/io/github/deltacv/vision/external/PipelineRenderHook.kt index 11171578..48e47f1d 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/PipelineRenderHook.kt +++ b/Vision/src/main/java/io/github/deltacv/vision/external/PipelineRenderHook.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external import android.graphics.Canvas @@ -40,3 +22,4 @@ object PipelineRenderHook : RenderHook { } } } + diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/gui/SkiaPanel.kt b/Vision/src/main/java/io/github/deltacv/vision/external/gui/SkiaPanel.kt index f5fad12d..2a61f7f0 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/gui/SkiaPanel.kt +++ b/Vision/src/main/java/io/github/deltacv/vision/external/gui/SkiaPanel.kt @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external.gui import org.jetbrains.skiko.ClipComponent @@ -31,4 +36,4 @@ class SkiaPanel(private val layer: SkiaLayer) : JLayeredPane() { layer.dispose() super.removeNotify() } -} \ No newline at end of file +} diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt b/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt index c478be35..21660e8f 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt +++ b/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt @@ -1,25 +1,8 @@ /* - * Copyright (c) 2023 OpenFTC Team & EOCV-Sim implementation by Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. */ + package io.github.deltacv.vision.external.gui import android.graphics.Bitmap @@ -416,4 +399,4 @@ class SwingOpenCvViewport( private const val FRAMEBUFFER_RECYCLER_CAPACITY = VISION_PREVIEW_FRAME_QUEUE_CAPACITY + 4 //So that the evicting queue can be full, and the render thread has one checked out (+1) and post() can still take one (+1). } -} \ No newline at end of file +} diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/gui/component/ImageX.java b/Vision/src/main/java/io/github/deltacv/vision/external/gui/component/ImageX.java index 0eb4b7d9..b216e769 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/gui/component/ImageX.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/gui/component/ImageX.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external.gui.component; import io.github.deltacv.vision.external.gui.util.ImgUtil; @@ -85,3 +67,4 @@ public void setSize(Dimension dimension) { } } + diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/gui/util/ImgUtil.java b/Vision/src/main/java/io/github/deltacv/vision/external/gui/util/ImgUtil.java index 9d146415..8a4583d7 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/gui/util/ImgUtil.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/gui/util/ImgUtil.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external.gui.util; import javax.swing.*; @@ -26,3 +31,4 @@ public static ImageIcon scaleImage(ImageIcon icon, int w, int h) { } } + diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/CameraControlMap.java b/Vision/src/main/java/io/github/deltacv/vision/external/source/CameraControlMap.java index c7cbdc3e..9aed0b9d 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/CameraControlMap.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/source/CameraControlMap.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external.source; import org.firstinspires.ftc.robotcore.external.hardware.camera.controls.CameraControl; @@ -6,4 +11,4 @@ public interface CameraControlMap { T get(Class classType); -} \ No newline at end of file +} diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/FrameReceiver.java b/Vision/src/main/java/io/github/deltacv/vision/external/source/FrameReceiver.java index 576d37b3..368407ab 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/FrameReceiver.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/source/FrameReceiver.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external.source; import org.opencv.core.Mat; @@ -31,3 +13,4 @@ default void onFrameStart() {} void stop(); } + diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/ThreadVisionSourceProvider.java b/Vision/src/main/java/io/github/deltacv/vision/external/source/ThreadVisionSourceProvider.java index a92b2f48..5b238573 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/ThreadVisionSourceProvider.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/source/ThreadVisionSourceProvider.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external.source; public final class ThreadVisionSourceProvider { @@ -42,3 +24,4 @@ public static VisionSource get(String name) { } } + diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/ViewportVisionSourceProvider.java b/Vision/src/main/java/io/github/deltacv/vision/external/source/ViewportVisionSourceProvider.java index 163bdf66..f415da34 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/ViewportVisionSourceProvider.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/source/ViewportVisionSourceProvider.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external.source; import org.openftc.easyopencv.OpenCvViewport; @@ -28,3 +10,4 @@ public interface ViewportVisionSourceProvider extends VisionSourceProvider { OpenCvViewport viewport(); } + diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSource.java b/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSource.java index e7a8afea..9071775d 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSource.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSource.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external.source; import org.opencv.core.Size; @@ -41,3 +23,4 @@ public interface VisionSource { boolean close(); } + diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceBase.java b/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceBase.java index b91af887..3b47bb56 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceBase.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceBase.java @@ -1,24 +1,6 @@ /* * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package io.github.deltacv.vision.external.source; @@ -162,3 +144,4 @@ public void run() { } } + diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceProvider.java b/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceProvider.java index eb848f17..0eee5b7d 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceProvider.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceProvider.java @@ -1,30 +1,12 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external.source; public interface VisionSourceProvider { VisionSource get(String name); -} \ No newline at end of file +} diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/util/CvUtil.java b/Vision/src/main/java/io/github/deltacv/vision/external/util/CvUtil.java index f0d11368..74d49050 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/util/CvUtil.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/util/CvUtil.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external.util; import io.github.deltacv.vision.external.util.extension.CvExt; @@ -202,3 +184,4 @@ public static Size scaleToFit(Size currentSize, Size targetSize) { } } + diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/util/FrameQueue.java b/Vision/src/main/java/io/github/deltacv/vision/external/util/FrameQueue.java index b4ba3cd4..d42e5287 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/util/FrameQueue.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/util/FrameQueue.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external.util; import org.firstinspires.ftc.robotcore.internal.collections.EvictingBlockingQueue; @@ -69,3 +51,4 @@ private void evict(Mat mat) { } } + diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/util/ThrowableHandler.java b/Vision/src/main/java/io/github/deltacv/vision/external/util/ThrowableHandler.java index 6adf22c5..67bae3ab 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/util/ThrowableHandler.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/util/ThrowableHandler.java @@ -1,5 +1,11 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external.util; public interface ThrowableHandler { void handle(Throwable e); } + diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/util/Timestamped.java b/Vision/src/main/java/io/github/deltacv/vision/external/util/Timestamped.java index 08230143..c35de870 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/util/Timestamped.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/util/Timestamped.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external.util; public class Timestamped { @@ -42,3 +24,4 @@ public long getTimestamp() { } } + diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/util/extension/CvExt.kt b/Vision/src/main/java/io/github/deltacv/vision/external/util/extension/CvExt.kt index cc04a96a..6e3dcd21 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/util/extension/CvExt.kt +++ b/Vision/src/main/java/io/github/deltacv/vision/external/util/extension/CvExt.kt @@ -1,25 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + @file:JvmName("CvExt") package io.github.deltacv.vision.external.util.extension @@ -49,4 +32,4 @@ fun Size.clipTo(size: Size): Size { width = Range.clip(width, 0.0, size.width) height = Range.clip(height, 0.0, size.height) return this -} \ No newline at end of file +} diff --git a/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/Enums.kt b/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/Enums.kt index 9b6e20cf..8934a750 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/Enums.kt +++ b/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/Enums.kt @@ -1,27 +1,9 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.internal.opmode enum class OpModeNotification { INIT, START, STOP, NOTHING } -enum class OpModeState { SELECTED, INIT, START, STOP, STOPPED } \ No newline at end of file +enum class OpModeState { SELECTED, INIT, START, STOP, STOPPED } diff --git a/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/OpModeNotifier.kt b/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/OpModeNotifier.kt index 9a13be42..782c1e60 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/OpModeNotifier.kt +++ b/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/OpModeNotifier.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.internal.opmode import com.github.serivesmejia.eocvsim.util.event.EventHandler @@ -88,4 +70,4 @@ class OpModeNotifier(maxNotificationsQueueSize: Int = 100) { fun pollException(): Throwable? = exceptionQueue.poll() -} \ No newline at end of file +} diff --git a/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/RedirectToOpModeThrowableHandler.kt b/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/RedirectToOpModeThrowableHandler.kt index 4de4ff6a..3a00571a 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/RedirectToOpModeThrowableHandler.kt +++ b/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/RedirectToOpModeThrowableHandler.kt @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.internal.opmode import io.github.deltacv.vision.external.util.ThrowableHandler @@ -29,4 +11,4 @@ class RedirectToOpModeThrowableHandler(private val notifier: OpModeNotifier) : T override fun handle(e: Throwable) { notifier.notify(e) } -} \ No newline at end of file +} diff --git a/Vision/src/main/java/io/github/deltacv/vision/internal/source/ftc/SourcedCameraName.java b/Vision/src/main/java/io/github/deltacv/vision/internal/source/ftc/SourcedCameraName.java index 8c189743..51b2906e 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/internal/source/ftc/SourcedCameraName.java +++ b/Vision/src/main/java/io/github/deltacv/vision/internal/source/ftc/SourcedCameraName.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.internal.source.ftc; import io.github.deltacv.vision.external.source.VisionSource; @@ -57,3 +39,4 @@ public CameraCharacteristics getCameraCharacteristics() { } } + diff --git a/Vision/src/main/java/io/github/deltacv/vision/internal/source/ftc/SourcedCameraNameImpl.java b/Vision/src/main/java/io/github/deltacv/vision/internal/source/ftc/SourcedCameraNameImpl.java index 3479190c..6ab76332 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/internal/source/ftc/SourcedCameraNameImpl.java +++ b/Vision/src/main/java/io/github/deltacv/vision/internal/source/ftc/SourcedCameraNameImpl.java @@ -1,26 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.internal.source.ftc; import androidx.annotation.NonNull; @@ -91,3 +73,4 @@ public boolean isAttached() { return false; } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/BuiltinCameraDirection.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/BuiltinCameraDirection.java index a4e85c55..1fa8940e 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/BuiltinCameraDirection.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/BuiltinCameraDirection.java @@ -1,7 +1,12 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package org.firstinspires.ftc.robotcore.external.hardware.camera; public enum BuiltinCameraDirection { BACK, FRONT -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraCharacteristics.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraCharacteristics.java index 3c9d5e8c..3216dc40 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraCharacteristics.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraCharacteristics.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2017 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2017 Robert Atkinson @@ -156,4 +161,4 @@ public CameraMode(int androidFormat, Size size, long nsFrameDuration, boolean is * @return all the combinatorial format, size, and fps camera modes supported */ List getAllCameraModes(); -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraControls.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraControls.java index ebb010d7..529a1f54 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraControls.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraControls.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2017 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2017 Robert Atkinson @@ -43,4 +48,4 @@ public interface CameraControls //---------------------------------------------------------------------------------------------- @Nullable T getControl(Class controlType); -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraName.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraName.java index c0012ea5..e3ab3546 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraName.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraName.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2017 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2017 Robert Atkinson @@ -81,4 +86,4 @@ public interface CameraName * @return The properties of the given camera. A degenerate empty set of properties is returned on error. */ CameraCharacteristics getCameraCharacteristics(); -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/WebcamName.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/WebcamName.java index 4bdd4ace..bbe43bec 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/WebcamName.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/WebcamName.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2018 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2018 Robert Atkinson @@ -60,4 +65,4 @@ public interface WebcamName extends CameraName, HardwareDevice * @return whether this camera currently attached to the robot controller */ boolean isAttached(); -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/CameraControl.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/CameraControl.java index 2201150a..453ad194 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/CameraControl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/CameraControl.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2018 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2018 Robert Atkinson @@ -39,4 +44,4 @@ are permitted (subject to the limitations in the disclaimer below) provided that @SuppressWarnings("WeakerAccess") public interface CameraControl { -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/ExposureControl.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/ExposureControl.java index da28eb73..a4cc5430 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/ExposureControl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/ExposureControl.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2018 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2018 Robert Atkinson @@ -140,3 +145,4 @@ public static Mode fromId(int id) */ boolean setAePriority(boolean priority); } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/FocusControl.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/FocusControl.java index 87c56189..0b839413 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/FocusControl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/FocusControl.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2018 Sebastian Erives + * Licensed under the MIT License. + */ + /* Copyright (c) 2018 Robert Atkinson @@ -115,3 +120,4 @@ public static FocusControl.Mode fromId(int index) */ boolean isFocusLengthSupported(); } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/GainControl.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/GainControl.java index 2d0b08d3..f3187c98 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/GainControl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/GainControl.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2020 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2020 Michael Hoogasian * @@ -66,3 +71,4 @@ public interface GainControl extends CameraControl */ boolean setGain(int gain); } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/PtzControl.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/PtzControl.java index d8da2cdc..b4bf1fb1 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/PtzControl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/PtzControl.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2020 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2020 Michael Hoogasian * @@ -99,4 +104,4 @@ class PanTiltHolder * @return the maximum virtual zoom level of the camera */ int getMaxZoom(); -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/WhiteBalanceControl.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/WhiteBalanceControl.java index 565a7210..24d4aa04 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/WhiteBalanceControl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/WhiteBalanceControl.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2021 Michael Hoogasian * @@ -84,3 +89,4 @@ enum Mode /* If you change this order, remember to update jni_devicehandle.cpp! */ boolean setWhiteBalanceTemperature(int temperature); } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortal.java b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortal.java index 01b9ad4c..9c38dc89 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortal.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortal.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2023 FIRST * @@ -470,4 +475,4 @@ public enum CameraState * A closed portal may not be re-opened: if you wish to use the camera again, you must make a new portal */ public abstract void close(); -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortalImpl.java b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortalImpl.java index ce1275e7..3ffb06d1 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortalImpl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortalImpl.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2023 FIRST * @@ -416,4 +421,4 @@ public void close() camera = null; } } -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionProcessor.java b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionProcessor.java index 253733b1..e1eac933 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionProcessor.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionProcessor.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2023 FIRST * @@ -37,3 +42,4 @@ * May be attached to a {@link VisionPortal} to run image processing */ public interface VisionProcessor extends VisionProcessorInternal {} + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionProcessorInternal.java b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionProcessorInternal.java index 3baf2bc6..32db8243 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionProcessorInternal.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionProcessorInternal.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2023 FIRST * @@ -46,4 +51,4 @@ interface VisionProcessorInternal void init(int width, int height, CameraCalibration calibration); Object processFrame(Mat frame, long captureTimeNanos); void onDrawFrame(Canvas canvas, int onscreenWidth, int onscreenHeight, float scaleBmpPxToCanvasPx, float scaleCanvasDensity, Object userContext); -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagCanvasAnnotator.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagCanvasAnnotator.java index 8e159b15..8d547c47 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagCanvasAnnotator.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagCanvasAnnotator.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2023 FIRST * @@ -292,3 +297,4 @@ void drawTagID(AprilTagDetection detection, Canvas canvas) canvas.restore(); } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagDetection.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagDetection.java index 3f0bbb8c..ace41dc6 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagDetection.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagDetection.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2023 FIRST * @@ -104,3 +109,4 @@ public AprilTagDetection(int id, int hamming, float decisionMargin, Point center this.frameAcquisitionNanoTime = frameAcquisitionNanoTime; } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagGameDatabase.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagGameDatabase.java index c5500a26..aad2ea8b 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagGameDatabase.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagGameDatabase.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2023 FIRST * @@ -158,3 +163,4 @@ public static AprilTagLibrary getSampleTagLibrary() .build(); } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagLibrary.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagLibrary.java index 29ef39bb..7cbf3565 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagLibrary.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagLibrary.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2023 FIRST * @@ -186,3 +191,4 @@ public AprilTagLibrary build() } } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagMetadata.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagMetadata.java index c1a25bfe..e3621c13 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagMetadata.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagMetadata.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2023 FIRST * @@ -82,3 +87,4 @@ public AprilTagMetadata(int id, String name, double tagsize, DistanceUnit distan this.distanceUnit = distanceUnit; } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagPoseFtc.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagPoseFtc.java index d9049246..c769005a 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagPoseFtc.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagPoseFtc.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2023 FIRST * @@ -113,4 +118,4 @@ public AprilTagPoseFtc(double x, double y, double z, double yaw, double pitch, this.bearing = bearing; this.elevation = elevation; } -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagPoseRaw.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagPoseRaw.java index f1fc3a94..b8b73268 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagPoseRaw.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagPoseRaw.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2023 FIRST * @@ -64,4 +69,4 @@ public AprilTagPoseRaw(double x, double y, double z, MatrixF R) this.z = z; this.R = R; } -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagProcessor.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagProcessor.java index 9ed97a89..65d63c23 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagProcessor.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagProcessor.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2023 FIRST * @@ -325,3 +330,4 @@ public enum PoseSolver public abstract ArrayList getFreshDetections(); } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagProcessorImpl.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagProcessorImpl.java index 34f44c10..6258fe2a 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagProcessorImpl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagProcessorImpl.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2023 FIRST * @@ -608,4 +613,4 @@ public Pose(Mat rvec, Mat tvec) this.tvec = tvec; } } -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/Circle.java b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/Circle.java index d02aa914..185e2b0c 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/Circle.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/Circle.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2025 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2025 Miriam Sinton-Remes * @@ -97,3 +102,4 @@ public float getY() { return y; } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorBlobLocatorProcessor.java b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorBlobLocatorProcessor.java index b4328196..3df83c69 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorBlobLocatorProcessor.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorBlobLocatorProcessor.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2024 FIRST * @@ -642,3 +647,4 @@ public int compare(Blob c1, Blob c2) } } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorBlobLocatorProcessorImpl.java b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorBlobLocatorProcessorImpl.java index 3466e20b..e37305a1 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorBlobLocatorProcessorImpl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorBlobLocatorProcessorImpl.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package org.firstinspires.ftc.vision.opencv; import android.graphics.Canvas; @@ -514,3 +519,4 @@ public Circle getCircle() } } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorRange.java b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorRange.java index 5aa95fcc..b921f3f9 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorRange.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorRange.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2024 FIRST * @@ -96,3 +101,4 @@ public ColorRange(ColorSpace colorSpace, Scalar min, Scalar max) this.max = max; } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorSpace.java b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorSpace.java index e21301ca..b2cf1fcd 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorSpace.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorSpace.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2024 FIRST * @@ -42,3 +47,4 @@ public enum ColorSpace HSV, RGB, } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ImageRegion.java b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ImageRegion.java index 83a65a13..b33a9760 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ImageRegion.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ImageRegion.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2024 FIRST * @@ -158,3 +163,4 @@ protected Rect asOpenCvRect(int imageWidth, int imageHeight) return rect; } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/PredominantColorProcessor.java b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/PredominantColorProcessor.java index 6596d42a..7d427903 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/PredominantColorProcessor.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/PredominantColorProcessor.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2024 FIRST * @@ -168,3 +173,4 @@ public static Swatch valueOf(int swatch) } } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/PredominantColorProcessorImpl.java b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/PredominantColorProcessorImpl.java index 01bf5568..2db9d546 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/PredominantColorProcessorImpl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/PredominantColorProcessorImpl.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + /* * Copyright (c) 2024 FIRST * @@ -260,3 +265,4 @@ byte[] yCrCb2Rgb(byte[] yCrCb) return rgb; } } + diff --git a/Vision/src/main/java/org/opencv/android/Utils.java b/Vision/src/main/java/org/opencv/android/Utils.java index 3aafb692..0a57a5bf 100644 --- a/Vision/src/main/java/org/opencv/android/Utils.java +++ b/Vision/src/main/java/org/opencv/android/Utils.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package org.opencv.android; import android.graphics.Bitmap; @@ -146,3 +151,4 @@ private static void nMatToBitmap2(Mat src, Bitmap b, boolean premultiplyAlpha) { tmp.release(); } } + diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCamera.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCamera.java index 8fa95646..843e040c 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCamera.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCamera.java @@ -1,24 +1,8 @@ -/* - * Copyright (c) 2019 OpenFTC Team - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - +/* + * Copyright (c) 2019 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv; public interface OpenCvCamera @@ -335,4 +319,4 @@ enum ViewportRenderer * if a recording session is currently active. */ void stopRecordingPipeline(); -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraBase.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraBase.java index 08ba161c..03462dac 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraBase.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraBase.java @@ -1,24 +1,8 @@ -/* - * Copyright (c) 2019 OpenFTC Team - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - +/* + * Copyright (c) 2019 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv; import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator; @@ -352,3 +336,4 @@ protected Size getFrameSizeAfterRotation(int width, int height, OpenCvCameraRota } } + diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraException.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraException.java index 72538e80..eddfe1b3 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraException.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraException.java @@ -1,24 +1,8 @@ -/* - * Copyright (c) 2019 OpenFTC Team - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - +/* + * Copyright (c) 2019 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv; public class OpenCvCameraException extends RuntimeException @@ -28,3 +12,4 @@ public OpenCvCameraException(String msg) super(msg); } } + diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraFactory.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraFactory.java index 896881af..4e43e4ac 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraFactory.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraFactory.java @@ -1,3 +1,8 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv; import org.firstinspires.ftc.robotcore.external.hardware.camera.WebcamName; @@ -35,3 +40,4 @@ public enum ViewportSplitMethod } } + diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraRotation.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraRotation.java index 4fd0d462..e45892fc 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraRotation.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraRotation.java @@ -1,24 +1,8 @@ -/* - * Copyright (c) 2019 OpenFTC Team - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - +/* + * Copyright (c) 2019 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv; public enum OpenCvCameraRotation @@ -28,4 +12,4 @@ public enum OpenCvCameraRotation SIDEWAYS_LEFT, SIDEWAYS_RIGHT, SENSOR_NATIVE, -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvInternalCamera.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvInternalCamera.java index ac0b15fd..644e2e8c 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvInternalCamera.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvInternalCamera.java @@ -1,24 +1,8 @@ -/* - * Copyright (c) 2019 OpenFTC Team - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - +/* + * Copyright (c) 2019 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv; import android.hardware.Camera; @@ -38,3 +22,4 @@ enum CameraDirection } } } + diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvInternalCamera2.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvInternalCamera2.java index 5c8a69c9..9a50515c 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvInternalCamera2.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvInternalCamera2.java @@ -1,24 +1,8 @@ -/* - * Copyright (c) 2019 OpenFTC Team - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - +/* + * Copyright (c) 2019 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv; import android.hardware.Camera; @@ -38,3 +22,4 @@ enum CameraDirection } } } + diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvTracker.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvTracker.java index a1422e4f..c66ed9f2 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvTracker.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvTracker.java @@ -1,24 +1,8 @@ -/* - * Copyright (c) 2019 OpenFTC Team - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - +/* + * Copyright (c) 2019 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv; import org.opencv.core.Mat; @@ -32,4 +16,4 @@ protected final Mat processFrameInternal(Mat input) { input.copyTo(mat); return processFrame(mat); } -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvTrackerApiPipeline.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvTrackerApiPipeline.java index 7f16da9a..f9936aac 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvTrackerApiPipeline.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvTrackerApiPipeline.java @@ -1,24 +1,8 @@ -/* - * Copyright (c) 2019 OpenFTC Team - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - +/* + * Copyright (c) 2019 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv; import com.qualcomm.robotcore.eventloop.opmode.Disabled; @@ -70,4 +54,4 @@ public synchronized void onViewportTapped() { trackerDisplayIdx = 0; } } -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvViewRenderer.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvViewRenderer.java index f46e34f6..58ede68d 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvViewRenderer.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvViewRenderer.java @@ -1,22 +1,6 @@ /* - * Copyright (c) 2023 OpenFTC Team - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. */ package org.openftc.easyopencv; @@ -336,4 +320,4 @@ public void renderPaused(Canvas canvas) canvas.drawText("VIEWPORT PAUSED", statBoxLTxtStart, textLine2Y, fpsMeterTextPaint); //canvas.drawText("Hi", statBoxLTxtStart, textLine3Y, fpsMeterTextPaint); } -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvViewport.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvViewport.java index f844ecd7..761126fd 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvViewport.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvViewport.java @@ -1,24 +1,8 @@ -/* - * Copyright (c) 2023 OpenFTC Team - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv; import android.graphics.Canvas; @@ -75,4 +59,4 @@ public FrameContext(OpenCvPipeline generatingPipeline, Object userContext) this.userContext = userContext; } } -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvWebcam.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvWebcam.java index d4d73b01..c342bd5e 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvWebcam.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvWebcam.java @@ -1,24 +1,8 @@ -/* - * Copyright (c) 2020 OpenFTC Team - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - +/* + * Copyright (c) 2020 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv; import org.firstinspires.ftc.robotcore.external.hardware.camera.controls.CameraControl; @@ -121,4 +105,4 @@ enum StreamFormat * @return the CameraCalibrationIdentity for this webcam */ CameraCalibrationIdentity getCalibrationIdentity(); -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/openftc/easyopencv/PipelineRecordingParameters.java b/Vision/src/main/java/org/openftc/easyopencv/PipelineRecordingParameters.java index f8427a09..40e02505 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/PipelineRecordingParameters.java +++ b/Vision/src/main/java/org/openftc/easyopencv/PipelineRecordingParameters.java @@ -1,24 +1,8 @@ -/* - * Copyright (c) 2020 OpenFTC Team - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - +/* + * Copyright (c) 2020 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv; import java.text.SimpleDateFormat; @@ -115,4 +99,4 @@ public PipelineRecordingParameters build() return new PipelineRecordingParameters(outputFormat, encoder, frameRate, bitrate, path); } } -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/openftc/easyopencv/SourcedOpenCvCameraFactoryImpl.java b/Vision/src/main/java/org/openftc/easyopencv/SourcedOpenCvCameraFactoryImpl.java index 14612724..4a6e41db 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/SourcedOpenCvCameraFactoryImpl.java +++ b/Vision/src/main/java/org/openftc/easyopencv/SourcedOpenCvCameraFactoryImpl.java @@ -1,24 +1,6 @@ /* * Copyright (c) 2023 Sebastian Erives - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * + * Licensed under the MIT License. */ package org.openftc.easyopencv; @@ -99,3 +81,4 @@ public OpenCvWebcam createWebcam(WebcamName cameraName, int viewportContainerId) } } + diff --git a/Vision/src/main/java/org/openftc/easyopencv/TimestampedOpenCvPipeline.java b/Vision/src/main/java/org/openftc/easyopencv/TimestampedOpenCvPipeline.java index e2453bae..a5c93070 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/TimestampedOpenCvPipeline.java +++ b/Vision/src/main/java/org/openftc/easyopencv/TimestampedOpenCvPipeline.java @@ -1,24 +1,8 @@ -/* - * Copyright (c) 2020 OpenFTC Team - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - +/* + * Copyright (c) 2020 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv; import org.opencv.core.Mat; @@ -39,4 +23,4 @@ protected void setTimestamp(long timestamp) { this.timestamp = timestamp; } -} \ No newline at end of file +} diff --git a/Vision/src/main/java/org/openftc/easyopencv/Util.java b/Vision/src/main/java/org/openftc/easyopencv/Util.java index 88801c96..d0cc1d12 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/Util.java +++ b/Vision/src/main/java/org/openftc/easyopencv/Util.java @@ -1,24 +1,8 @@ -/* - * Copyright (c) 2023 OpenFTC Team - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package org.openftc.easyopencv; import java.util.concurrent.CountDownLatch; @@ -72,4 +56,4 @@ public static void acquireUninterruptibly(CountDownLatch latch) Thread.currentThread().interrupt(); } } -} \ No newline at end of file +} diff --git a/build.gradle b/build.gradle index ea750706..92d8990b 100644 --- a/build.gradle +++ b/build.gradle @@ -58,7 +58,7 @@ ext { allprojects { group 'org.deltacv.EOCV-Sim' - version '4.1.2' + version '4.2.0' apply plugin: 'java' From 26c5e7bcb50f89c02492dd3760cfbaf8f320d9a4 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 12 May 2026 19:56:21 -0600 Subject: [PATCH 19/40] Add multiplatform releases in actions --- .github/workflows/build_ci.yml | 81 ++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build_ci.yml b/.github/workflows/build_ci.yml index dc35e1f4..3d687700 100644 --- a/.github/workflows/build_ci.yml +++ b/.github/workflows/build_ci.yml @@ -1,6 +1,11 @@ -name: Build and test with Gradle +name: Build, Test & Release + +on: + push: + branches: [ master, dev ] + tags: 'v*' + pull_request: -on: [push, pull_request] jobs: test_linux: @@ -16,7 +21,14 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build and test with Gradle - run: ./gradlew test + run: ./gradlew -Penv=ci clean build shadowJar + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: eocvsim-ubuntu-latest + path: | + **/build/libs/*.jar + **/build/reports test_windows: runs-on: windows-latest @@ -28,10 +40,16 @@ jobs: with: distribution: 'zulu' java-version: '25' - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - name: Build and test with Gradle - run: ./gradlew test + - name: Build and test with Gradle (Windows) + shell: pwsh + run: .\gradlew.bat -Penv=ci clean build shadowJar + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: eocvsim-windows-latest + path: | + **/build/libs/*.jar + **/build/reports test_mac: runs-on: macos-latest @@ -46,4 +64,51 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build and test with Gradle - run: ./gradlew test + run: ./gradlew -Penv=ci clean build shadowJar + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: eocvsim-macos-latest + path: | + **/build/libs/*.jar + **/build/reports + + release: + needs: [test_linux, test_windows, test_mac] + runs-on: ubuntu-latest + if: startsWith(github.ref, 'refs/tags/v') + steps: + - uses: actions/checkout@v2 + + - name: Download Linux artifacts + uses: actions/download-artifact@v4 + with: + name: eocvsim-ubuntu-latest + path: artifacts/ubuntu + + - name: Download macOS artifacts + uses: actions/download-artifact@v4 + with: + name: eocvsim-macos-latest + path: artifacts/macos + + - name: Download Windows artifacts + uses: actions/download-artifact@v4 + with: + name: eocvsim-windows-latest + path: artifacts/windows + + - name: List downloaded artifacts + run: | + ls -la artifacts || true + ls -la artifacts/ubuntu || true + ls -la artifacts/macos || true + ls -la artifacts/windows || true + + - name: Create GitHub Release and upload artifacts + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ github.ref_name }} + files: artifacts/**/*.jar + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From fdefb727d333cf7873aed83c705edd3974228bbd Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 12 May 2026 20:23:01 -0600 Subject: [PATCH 20/40] Fix CI build parameters & consolidate maven publish into single file --- .github/workflows/build_ci.yml | 54 ++++++++++++++++++++++++++++--- .github/workflows/release_ci.yml | 55 -------------------------------- 2 files changed, 50 insertions(+), 59 deletions(-) delete mode 100644 .github/workflows/release_ci.yml diff --git a/.github/workflows/build_ci.yml b/.github/workflows/build_ci.yml index 3d687700..b2ffc9f0 100644 --- a/.github/workflows/build_ci.yml +++ b/.github/workflows/build_ci.yml @@ -3,13 +3,15 @@ name: Build, Test & Release on: push: branches: [ master, dev ] - tags: 'v*' + tags: ['v*'] pull_request: jobs: test_linux: runs-on: ubuntu-latest + env: + BUILD_ENV: ${{ startsWith(github.ref, 'refs/tags/v') && 'release' || 'dev' }} steps: - uses: actions/checkout@v2 @@ -21,7 +23,7 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build and test with Gradle - run: ./gradlew -Penv=ci clean build shadowJar + run: ./gradlew -Penv=${{ env.BUILD_ENV }} clean build shadowJar - name: Upload build artifacts uses: actions/upload-artifact@v4 with: @@ -32,6 +34,8 @@ jobs: test_windows: runs-on: windows-latest + env: + BUILD_ENV: ${{ startsWith(github.ref, 'refs/tags/v') && 'release' || 'dev' }} steps: - uses: actions/checkout@v2 @@ -42,7 +46,7 @@ jobs: java-version: '25' - name: Build and test with Gradle (Windows) shell: pwsh - run: .\gradlew.bat -Penv=ci clean build shadowJar + run: .\gradlew.bat -Penv=${{ env.BUILD_ENV }} clean build shadowJar - name: Upload build artifacts uses: actions/upload-artifact@v4 with: @@ -53,6 +57,8 @@ jobs: test_mac: runs-on: macos-latest + env: + BUILD_ENV: ${{ startsWith(github.ref, 'refs/tags/v') && 'release' || 'dev' }} steps: - uses: actions/checkout@v2 @@ -64,7 +70,7 @@ jobs: - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build and test with Gradle - run: ./gradlew -Penv=ci clean build shadowJar + run: ./gradlew -Penv=${{ env.BUILD_ENV }} clean build shadowJar - name: Upload build artifacts uses: actions/upload-artifact@v4 with: @@ -80,6 +86,26 @@ jobs: steps: - uses: actions/checkout@v2 + - name: Set up JDK + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: '25' + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Publish package to maven central + env: + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.JRELEASER_MAVENCENTRAL_USERNAME }} + 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 -x :EOCV-Sim:shadowJar + + - name: Build release shadow jar with Gradle + run: ./gradlew -Penv=release shadowJar -x test + - name: Download Linux artifacts uses: actions/download-artifact@v4 with: @@ -112,3 +138,23 @@ jobs: files: artifacts/**/*.jar env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + dev_release: + needs: [test_linux] + runs-on: ubuntu-latest + if: github.event_name == 'push' && github.ref != 'refs/heads/master' && !startsWith(github.ref, 'refs/tags/v') + steps: + - name: Download Linux artifacts + uses: actions/download-artifact@v4 + with: + name: eocvsim-ubuntu-latest + path: artifacts/dev + + - name: Create Dev Release and upload artifacts + uses: pyTooling/Actions/releaser@r0 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag: 'Dev' + rm: true + files: | + artifacts/dev/**/*.jar diff --git a/.github/workflows/release_ci.yml b/.github/workflows/release_ci.yml deleted file mode 100644 index 224bdf88..00000000 --- a/.github/workflows/release_ci.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: Maven Publish & Create GitHub Release(s) - -on: - push: - branches: [ master, dev ] - tags: 'v*' - -jobs: - build-and-release: - if: ${{ startsWith(github.ref, 'refs/tags/v') || github.ref != 'ref/heads/master' }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up JDK - uses: actions/setup-java@v3 - with: - distribution: 'zulu' - java-version: '25' - - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - - name: Publish package to maven central - env: - ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.JRELEASER_MAVENCENTRAL_USERNAME }} - 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 -x :EOCV-Sim:shadowJar - if: ${{ startsWith(github.ref, 'refs/tags/v') }} - - - name: Build release shadow jar with Gradle - run: ./gradlew -Penv=release shadowJar -x test - if: ${{ startsWith(github.ref, 'refs/tags/v') }} - - - name: Build dev shadow jar with Gradle - run: | - SHA_SHORT="$(git rev-parse --short HEAD)" - ./gradlew -Phash=$SHA_SHORT shadowJar -x test - if: ${{ !startsWith(github.ref, 'refs/tags/v') && github.ref != 'refs/heads/master' }} - - - uses: pyTooling/Actions/releaser@r0 - with: - token: ${{ secrets.GITHUB_TOKEN }} - tag: 'Dev' - rm: true - files: | - EOCV-Sim/build/libs/*.jar - if: ${{ github.event_name == 'push' && github.ref != 'refs/heads/master' && !startsWith(github.ref, 'refs/tags/v')}} - - - uses: softprops/action-gh-release@v1 - if: ${{ startsWith(github.ref, 'refs/tags/v') }} - with: - token: ${{ secrets.GITHUB_TOKEN }} - files: 'EOCV-Sim/build/libs/*.jar' From 791df8110f61396cc0205a4bd6c5c6ada1b1ef7c Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 12 May 2026 20:30:42 -0600 Subject: [PATCH 21/40] Fix build job & include all three platforms on dev release --- .github/workflows/build_ci.yml | 43 ++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build_ci.yml b/.github/workflows/build_ci.yml index b2ffc9f0..4eac75c4 100644 --- a/.github/workflows/build_ci.yml +++ b/.github/workflows/build_ci.yml @@ -29,8 +29,7 @@ jobs: with: name: eocvsim-ubuntu-latest path: | - **/build/libs/*.jar - **/build/reports + EOCV-Sim/build/libs/*.jar test_windows: runs-on: windows-latest @@ -52,8 +51,7 @@ jobs: with: name: eocvsim-windows-latest path: | - **/build/libs/*.jar - **/build/reports + EOCV-Sim/build/libs/*.jar test_mac: runs-on: macos-latest @@ -76,8 +74,7 @@ jobs: with: name: eocvsim-macos-latest path: | - **/build/libs/*.jar - **/build/reports + EOCV-Sim/build/libs/*.jar release: needs: [test_linux, test_windows, test_mac] @@ -131,16 +128,23 @@ jobs: ls -la artifacts/macos || true ls -la artifacts/windows || true + - name: Filter main release jars only + run: | + mkdir -p release-jars + find artifacts -name "*EOCV-Sim-*.jar" ! -name "*-sources.jar" ! -name "*-linux*" ! -name "*-windows*" ! -name "*-mac*" -exec cp {} release-jars/ \; + echo "Release jars to upload:" + ls -la release-jars/ + - name: Create GitHub Release and upload artifacts uses: softprops/action-gh-release@v1 with: tag_name: ${{ github.ref_name }} - files: artifacts/**/*.jar + files: release-jars/*.jar env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} dev_release: - needs: [test_linux] + needs: [test_linux, test_windows, test_mac] runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref != 'refs/heads/master' && !startsWith(github.ref, 'refs/tags/v') steps: @@ -148,7 +152,26 @@ jobs: uses: actions/download-artifact@v4 with: name: eocvsim-ubuntu-latest - path: artifacts/dev + path: artifacts/dev/ubuntu + + - name: Download macOS artifacts + uses: actions/download-artifact@v4 + with: + name: eocvsim-macos-latest + path: artifacts/dev/macos + + - name: Download Windows artifacts + uses: actions/download-artifact@v4 + with: + name: eocvsim-windows-latest + path: artifacts/dev/windows + + - name: Filter main release jars only for dev + run: | + mkdir -p release-jars + find artifacts/dev -name "*EOCV-Sim-*.jar" ! -name "*-sources.jar" ! -name "*-linux*" ! -name "*-windows*" ! -name "*-mac*" -exec cp {} release-jars/ \; + echo "Dev release jars to upload:" + ls -la release-jars/ - name: Create Dev Release and upload artifacts uses: pyTooling/Actions/releaser@r0 @@ -157,4 +180,4 @@ jobs: tag: 'Dev' rm: true files: | - artifacts/dev/**/*.jar + release-jars/*.jar From 768de59652f11b70c7823dd50e53bf32fd0dad67 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 12 May 2026 20:37:31 -0600 Subject: [PATCH 22/40] Properly upload all artifacts to release --- .github/workflows/build_ci.yml | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_ci.yml b/.github/workflows/build_ci.yml index 4eac75c4..7bdecc13 100644 --- a/.github/workflows/build_ci.yml +++ b/.github/workflows/build_ci.yml @@ -128,10 +128,15 @@ jobs: ls -la artifacts/macos || true ls -la artifacts/windows || true - - name: Filter main release jars only + - name: Select platform jars for release run: | mkdir -p release-jars - find artifacts -name "*EOCV-Sim-*.jar" ! -name "*-sources.jar" ! -name "*-linux*" ! -name "*-windows*" ! -name "*-mac*" -exec cp {} release-jars/ \; + # Copy the first (and should be only) jar from each platform folder + cp artifacts/ubuntu/EOCV-Sim-*.jar release-jars/ 2>/dev/null || echo "No Linux jar found" + cp artifacts/windows/EOCV-Sim-*.jar release-jars/ 2>/dev/null || echo "No Windows jar found" + cp artifacts/macos/EOCV-Sim-*.jar release-jars/ 2>/dev/null || echo "No macOS jar found" + # Remove source jars if any were copied + rm -f release-jars/*-sources.jar echo "Release jars to upload:" ls -la release-jars/ @@ -166,10 +171,15 @@ jobs: name: eocvsim-windows-latest path: artifacts/dev/windows - - name: Filter main release jars only for dev + - name: Select platform jars for dev release run: | mkdir -p release-jars - find artifacts/dev -name "*EOCV-Sim-*.jar" ! -name "*-sources.jar" ! -name "*-linux*" ! -name "*-windows*" ! -name "*-mac*" -exec cp {} release-jars/ \; + # Copy the first (and should be only) jar from each platform folder + cp artifacts/dev/ubuntu/EOCV-Sim-*.jar release-jars/ 2>/dev/null || echo "No Linux jar found" + cp artifacts/dev/windows/EOCV-Sim-*.jar release-jars/ 2>/dev/null || echo "No Windows jar found" + cp artifacts/dev/macos/EOCV-Sim-*.jar release-jars/ 2>/dev/null || echo "No macOS jar found" + # Remove source jars if any were copied + rm -f release-jars/*-sources.jar echo "Dev release jars to upload:" ls -la release-jars/ From e155f60fdc31358e89013d685cd0a7238d27ea6e Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 12 May 2026 22:36:45 -0600 Subject: [PATCH 23/40] Add Linux arm64 and Intel mac --- .github/workflows/build_ci.yml | 86 ++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_ci.yml b/.github/workflows/build_ci.yml index 7bdecc13..6ef48ddf 100644 --- a/.github/workflows/build_ci.yml +++ b/.github/workflows/build_ci.yml @@ -1,4 +1,4 @@ -name: Build, Test & Release + name: Build, Test & Release on: push: @@ -31,6 +31,29 @@ jobs: path: | EOCV-Sim/build/libs/*.jar + test_linux_arm: + runs-on: ubuntu-latest-arm64 + env: + BUILD_ENV: ${{ startsWith(github.ref, 'refs/tags/v') && 'release' || 'dev' }} + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: '25' + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build and test with Gradle + run: ./gradlew -Penv=${{ env.BUILD_ENV }} clean build shadowJar + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: eocvsim-ubuntu-arm64 + path: | + EOCV-Sim/build/libs/*.jar + test_windows: runs-on: windows-latest env: @@ -76,8 +99,31 @@ jobs: path: | EOCV-Sim/build/libs/*.jar + test_mac_intel: + runs-on: macos-13 + env: + BUILD_ENV: ${{ startsWith(github.ref, 'refs/tags/v') && 'release' || 'dev' }} + + steps: + - uses: actions/checkout@v2 + - name: Set up JDK + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: '25' + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build and test with Gradle + run: ./gradlew -Penv=${{ env.BUILD_ENV }} clean build shadowJar + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: eocvsim-macos-13 + path: | + EOCV-Sim/build/libs/*.jar + release: - needs: [test_linux, test_windows, test_mac] + needs: [test_linux, test_linux_arm, test_windows, test_mac, test_mac_intel] runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/v') steps: @@ -109,12 +155,24 @@ jobs: name: eocvsim-ubuntu-latest path: artifacts/ubuntu + - name: Download Linux ARM64 artifacts + uses: actions/download-artifact@v4 + with: + name: eocvsim-ubuntu-arm64 + path: artifacts/ubuntu-arm64 + - name: Download macOS artifacts uses: actions/download-artifact@v4 with: name: eocvsim-macos-latest path: artifacts/macos + - name: Download Intel macOS artifacts + uses: actions/download-artifact@v4 + with: + name: eocvsim-macos-13 + path: artifacts/macos-intel + - name: Download Windows artifacts uses: actions/download-artifact@v4 with: @@ -125,7 +183,9 @@ jobs: run: | ls -la artifacts || true ls -la artifacts/ubuntu || true + ls -la artifacts/ubuntu-arm64 || true ls -la artifacts/macos || true + ls -la artifacts/macos-intel || true ls -la artifacts/windows || true - name: Select platform jars for release @@ -133,10 +193,14 @@ jobs: mkdir -p release-jars # Copy the first (and should be only) jar from each platform folder cp artifacts/ubuntu/EOCV-Sim-*.jar release-jars/ 2>/dev/null || echo "No Linux jar found" + cp artifacts/ubuntu-arm64/EOCV-Sim-*.jar release-jars/ 2>/dev/null || echo "No Linux ARM64 jar found" cp artifacts/windows/EOCV-Sim-*.jar release-jars/ 2>/dev/null || echo "No Windows jar found" cp artifacts/macos/EOCV-Sim-*.jar release-jars/ 2>/dev/null || echo "No macOS jar found" + cp artifacts/macos-intel/EOCV-Sim-*.jar release-jars/ 2>/dev/null || echo "No Intel macOS jar found" # Remove source jars if any were copied rm -f release-jars/*-sources.jar + # Remove the generic jar (no platform suffix) + find release-jars -name "EOCV-Sim-*.jar" ! -name "*x86*" ! -name "*arm*" ! -name "*win*" -delete echo "Release jars to upload:" ls -la release-jars/ @@ -149,7 +213,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} dev_release: - needs: [test_linux, test_windows, test_mac] + needs: [test_linux, test_linux_arm, test_windows, test_mac, test_mac_intel] runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref != 'refs/heads/master' && !startsWith(github.ref, 'refs/tags/v') steps: @@ -159,6 +223,12 @@ jobs: name: eocvsim-ubuntu-latest path: artifacts/dev/ubuntu + - name: Download Linux ARM64 artifacts + uses: actions/download-artifact@v4 + with: + name: eocvsim-ubuntu-arm64 + path: artifacts/dev/ubuntu-arm64 + - name: Download macOS artifacts uses: actions/download-artifact@v4 with: @@ -171,15 +241,25 @@ jobs: name: eocvsim-windows-latest path: artifacts/dev/windows + - name: Download Intel macOS artifacts + uses: actions/download-artifact@v4 + with: + name: eocvsim-macos-13 + path: artifacts/dev/macos-intel + - name: Select platform jars for dev release run: | mkdir -p release-jars # Copy the first (and should be only) jar from each platform folder cp artifacts/dev/ubuntu/EOCV-Sim-*.jar release-jars/ 2>/dev/null || echo "No Linux jar found" + cp artifacts/dev/ubuntu-arm64/EOCV-Sim-*.jar release-jars/ 2>/dev/null || echo "No Linux ARM64 jar found" cp artifacts/dev/windows/EOCV-Sim-*.jar release-jars/ 2>/dev/null || echo "No Windows jar found" cp artifacts/dev/macos/EOCV-Sim-*.jar release-jars/ 2>/dev/null || echo "No macOS jar found" + cp artifacts/dev/macos-intel/EOCV-Sim-*.jar release-jars/ 2>/dev/null || echo "No Intel macOS jar found" # Remove source jars if any were copied rm -f release-jars/*-sources.jar + # Remove the generic jar (no platform suffix) + find release-jars -name "EOCV-Sim-*.jar" ! -name "*x86*" ! -name "*arm*" ! -name "*win*" -delete echo "Dev release jars to upload:" ls -la release-jars/ From 8d90f4af7cd3dda4c39da86fc86cad5cc9b59944 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 12 May 2026 22:41:22 -0600 Subject: [PATCH 24/40] Fix broken spaces on workflow file --- .github/workflows/build_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_ci.yml b/.github/workflows/build_ci.yml index 6ef48ddf..1aa34260 100644 --- a/.github/workflows/build_ci.yml +++ b/.github/workflows/build_ci.yml @@ -1,4 +1,4 @@ - name: Build, Test & Release +name: Build, Test & Release on: push: From a8cf7268279039458c6ec9a5e8e61b2287b5cdf9 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 12 May 2026 22:52:56 -0600 Subject: [PATCH 25/40] Fix runner images --- .github/workflows/build_ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_ci.yml b/.github/workflows/build_ci.yml index 1aa34260..18d5274b 100644 --- a/.github/workflows/build_ci.yml +++ b/.github/workflows/build_ci.yml @@ -9,7 +9,7 @@ on: jobs: test_linux: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 env: BUILD_ENV: ${{ startsWith(github.ref, 'refs/tags/v') && 'release' || 'dev' }} @@ -32,7 +32,7 @@ jobs: EOCV-Sim/build/libs/*.jar test_linux_arm: - runs-on: ubuntu-latest-arm64 + runs-on: ubuntu-24.04-arm64 env: BUILD_ENV: ${{ startsWith(github.ref, 'refs/tags/v') && 'release' || 'dev' }} @@ -77,7 +77,7 @@ jobs: EOCV-Sim/build/libs/*.jar test_mac: - runs-on: macos-latest + runs-on: macos-26 env: BUILD_ENV: ${{ startsWith(github.ref, 'refs/tags/v') && 'release' || 'dev' }} @@ -100,7 +100,7 @@ jobs: EOCV-Sim/build/libs/*.jar test_mac_intel: - runs-on: macos-13 + runs-on: macos-26-intel env: BUILD_ENV: ${{ startsWith(github.ref, 'refs/tags/v') && 'release' || 'dev' }} From 2ccd3bae1168590307729895caa1ea0d21466ceb Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 12 May 2026 22:56:02 -0600 Subject: [PATCH 26/40] Fix ubuntu arm image name --- .github/workflows/build_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_ci.yml b/.github/workflows/build_ci.yml index 18d5274b..1b263f89 100644 --- a/.github/workflows/build_ci.yml +++ b/.github/workflows/build_ci.yml @@ -32,7 +32,7 @@ jobs: EOCV-Sim/build/libs/*.jar test_linux_arm: - runs-on: ubuntu-24.04-arm64 + runs-on: ubuntu-24.04-arm env: BUILD_ENV: ${{ startsWith(github.ref, 'refs/tags/v') && 'release' || 'dev' }} From b2cfc2f6421874a63ca6cecaeb7764277967b807 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Wed, 13 May 2026 01:48:32 -0600 Subject: [PATCH 27/40] Add Bootstrap that checks for Java version instead of silently failing --- .github/workflows/build_ci.yml | 10 +- EOCV-Sim/build.gradle | 19 + .../serivesmejia/eocvsim/Bootstrap.java | 343 ++++++++++++++++++ .../com/github/serivesmejia/eocvsim/Main.kt | 12 +- .../eocvsim/RuntimeVersionGuard.java | 81 +++++ build.gradle | 6 - 6 files changed, 455 insertions(+), 16 deletions(-) create mode 100644 EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java create mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/RuntimeVersionGuard.java diff --git a/.github/workflows/build_ci.yml b/.github/workflows/build_ci.yml index 1b263f89..39ab494e 100644 --- a/.github/workflows/build_ci.yml +++ b/.github/workflows/build_ci.yml @@ -8,7 +8,7 @@ on: jobs: - test_linux: + build_linux: runs-on: ubuntu-24.04 env: BUILD_ENV: ${{ startsWith(github.ref, 'refs/tags/v') && 'release' || 'dev' }} @@ -31,7 +31,7 @@ jobs: path: | EOCV-Sim/build/libs/*.jar - test_linux_arm: + build_linux_arm: runs-on: ubuntu-24.04-arm env: BUILD_ENV: ${{ startsWith(github.ref, 'refs/tags/v') && 'release' || 'dev' }} @@ -54,7 +54,7 @@ jobs: path: | EOCV-Sim/build/libs/*.jar - test_windows: + build_windows: runs-on: windows-latest env: BUILD_ENV: ${{ startsWith(github.ref, 'refs/tags/v') && 'release' || 'dev' }} @@ -76,7 +76,7 @@ jobs: path: | EOCV-Sim/build/libs/*.jar - test_mac: + build_mac: runs-on: macos-26 env: BUILD_ENV: ${{ startsWith(github.ref, 'refs/tags/v') && 'release' || 'dev' }} @@ -99,7 +99,7 @@ jobs: path: | EOCV-Sim/build/libs/*.jar - test_mac_intel: + build_mac_intel: runs-on: macos-26-intel env: BUILD_ENV: ${{ startsWith(github.ref, 'refs/tags/v') && 'release' || 'dev' }} diff --git a/EOCV-Sim/build.gradle b/EOCV-Sim/build.gradle index 208f5490..849fa43e 100644 --- a/EOCV-Sim/build.gradle +++ b/EOCV-Sim/build.gradle @@ -24,11 +24,30 @@ sourceSets { srcDirs += generatedSourcesDir } } + + bootstrap { + java { + srcDirs = ['src/bootstrap/java'] + } + } +} + +tasks.named('compileBootstrapJava', JavaCompile) { + options.release.set(8) } shadowJar { mergeServiceFiles() archiveClassifier.set(wpilibTools.currentPlatform.platformName) + + from(sourceSets.main.output) + from(sourceSets.bootstrap.output) + + manifest { + attributes( + 'Main-Class': 'com.github.serivesmejia.eocvsim.Bootstrap' + ) + } } test { diff --git a/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java b/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java new file mode 100644 index 00000000..578e6e2d --- /dev/null +++ b/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java @@ -0,0 +1,343 @@ +package com.github.serivesmejia.eocvsim; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Bootstrap { + + public static void main(String[] args) throws Exception { + log("======================================"); + log("Starting EOCV-Sim Bootstrap"); + log("======================================"); + + log("Runtime:"); + log(" java.version = " + System.getProperty("java.version")); + log(" os.name = " + System.getProperty("os.name")); + log(" java.home = " + System.getProperty("java.home")); + log(" JAVA_HOME = " + System.getenv("JAVA_HOME")); + + if (Boolean.getBoolean("eocvsim.bypass.bootstrap")) { + log("Bootstrap bypass enabled"); + launchApp(args); + return; + } + + int current = getJavaMajorVersion(System.getProperty("java.version")); + log("Detected runtime Java major: " + current); + + if (current >= 25) { + log("Java 25+ detected → launching directly"); + launchApp(args); + return; + } + + log("Java 25+ NOT detected"); + + File detected = findJava25(); + + if (detected != null) { + log("Autodetected Java 25+: " + detected.getAbsolutePath()); + } else { + log("No Java 25+ installations detected"); + } + + File chosen = promptUser(detected, current); + + if (chosen == null) { + log("User exited bootstrap"); + System.exit(0); + return; + } + + log("User selected: " + chosen.getAbsolutePath()); + + relaunch(chosen, args); + } + + // ---------------- UI ---------------- + + private static File promptUser(File detected, int currentJava) { + + boolean hasDetected = detected != null; + + log("Opening selection UI (detected=" + hasDetected + ")"); + + final File[] result = new File[1]; + final Object lock = new Object(); + + JFrame dialog = new JFrame("EOCV-Sim: Java 25 Required"); + dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + + JPanel root = new JPanel(); + root.setBorder(BorderFactory.createEmptyBorder(14, 16, 14, 16)); + root.setLayout(new BoxLayout(root, BoxLayout.Y_AXIS)); + + // ---------------- TITLE ---------------- + JLabel title = new JLabel("EOCV-Sim requires Java 25 or newer"); + title.setAlignmentX(Component.CENTER_ALIGNMENT); + title.setHorizontalAlignment(SwingConstants.CENTER); + title.setFont(title.getFont().deriveFont(Font.BOLD, 16f)); + + // ---------------- INFO TEXT ---------------- + JTextArea info = new JTextArea(); + info.setEditable(false); + info.setLineWrap(true); + info.setWrapStyleWord(true); + info.setOpaque(false); + info.setFocusable(false); + + StringBuilder text = new StringBuilder(); + text.append("EOCV-Sim was started with Java ") + .append(currentJava) + .append(", but requires Java 25 or newer to run.\n\n"); + if (!hasDetected) { + text.append("No compatible Java installation was found automatically.\n\n"); + } + text.append("Select a Java 25+ installation or continue with the detected one if it is correct."); + info.setText(text.toString()); + info.setMaximumSize(new Dimension(420, 120)); + + // ---------------- DETECTED AREAS ---------------- + JTextArea detectedLabel = null; + JTextArea detectedPath = null; + + if (hasDetected) { + detectedLabel = new JTextArea("Autodetected installation:"); + detectedLabel.setEditable(false); + detectedLabel.setOpaque(false); + detectedLabel.setFocusable(false); + detectedLabel.setFont(info.getFont().deriveFont(Font.BOLD)); + + detectedPath = new JTextArea(detected.getAbsolutePath()); + detectedPath.setEditable(false); + detectedPath.setOpaque(false); + detectedPath.setFocusable(false); + detectedPath.setFont(info.getFont().deriveFont(Font.BOLD)); + detectedPath.setForeground(new Color(0, 120, 215)); + } + + // ---------------- BUTTONS ---------------- + JButton continueBtn = new JButton("Continue with detected"); + JButton selectBtn = new JButton("Select from disk"); + JButton exitBtn = new JButton("Exit"); + + continueBtn.setEnabled(hasDetected); + + continueBtn.setPreferredSize(new Dimension(180, 30)); + selectBtn.setPreferredSize(new Dimension(160, 30)); + exitBtn.setPreferredSize(new Dimension(360, 30)); + + // ---------------- ACTIONS ---------------- + continueBtn.addActionListener(e -> { + log("User: continue with detected"); + result[0] = detected; + dialog.dispose(); + synchronized (lock) { lock.notifyAll(); } + }); + + selectBtn.addActionListener(e -> { + log("User: browse disk"); + + JFileChooser chooser = new JFileChooser(); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + + if (chooser.showOpenDialog(dialog) == JFileChooser.APPROVE_OPTION) { + File selected = chooser.getSelectedFile(); + log("Selected: " + selected.getAbsolutePath()); + + if (isJava25OrNewer(selected)) { + log("Valid Java 25+ selected"); + result[0] = selected; + dialog.dispose(); + synchronized (lock) { lock.notifyAll(); } + } else { + log("Invalid Java selected"); + JOptionPane.showMessageDialog( + dialog, + "This directory is not a valid Java 25 or newer installation.", + "Invalid Java", + JOptionPane.ERROR_MESSAGE + ); + } + } + }); + + exitBtn.addActionListener(e -> { + log("User exit"); + result[0] = null; + dialog.dispose(); + synchronized (lock) { lock.notifyAll(); } + }); + + dialog.addWindowListener(new WindowAdapter() { + @Override + public void windowClosed(WindowEvent e) { + synchronized (lock) { lock.notifyAll(); } + } + }); + + // ---------------- LAYOUT ---------------- + root.add(title); + root.add(Box.createVerticalStrut(10)); + root.add(info); + + if (hasDetected) { + root.add(Box.createVerticalStrut(8)); + root.add(detectedLabel); + root.add(Box.createVerticalStrut(2)); + root.add(detectedPath); + } + + root.add(Box.createVerticalStrut(12)); + + JPanel row1 = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0)); + row1.add(continueBtn); + row1.add(selectBtn); + + JPanel row2 = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 8)); + row2.add(exitBtn); + + JPanel buttonBlock = new JPanel(); + buttonBlock.setLayout(new BoxLayout(buttonBlock, BoxLayout.Y_AXIS)); + buttonBlock.add(row1); + buttonBlock.add(row2); + + root.add(buttonBlock); + + dialog.setContentPane(root); + + dialog.setMinimumSize(new Dimension(460, 260)); + dialog.pack(); + dialog.setLocationRelativeTo(null); + dialog.setResizable(false); + + dialog.setVisible(true); + + synchronized (lock) { + try { lock.wait(); } catch (InterruptedException ignored) {} + } + + return result[0]; + } + + // ---------------- LAUNCH ---------------- + + private static void launchApp(String[] args) throws Exception { + log("Launching Main via reflection"); + + Class.forName("com.github.serivesmejia.eocvsim.Main") + .getMethod("main", String[].class) + .invoke(null, (Object) args); + } + + private static void relaunch(File javaHome, String[] args) throws IOException { + String os = System.getProperty("os.name").toLowerCase(); + + String javaBin = javaHome.getAbsolutePath() + + File.separator + "bin" + + File.separator + "java"; + + if (os.contains("win")) javaBin += ".exe"; + + File jar = new File( + Bootstrap.class.getProtectionDomain() + .getCodeSource() + .getLocation() + .getPath() + ); + + log("Relaunching:"); + log(" Java: " + javaBin); + log(" Jar : " + jar.getAbsolutePath()); + + List cmd = new ArrayList<>(); + cmd.add(javaBin); + cmd.add("-cp"); + cmd.add(jar.getAbsolutePath()); + cmd.add("com.github.serivesmejia.eocvsim.Main"); + + Collections.addAll(cmd, args); + + new ProcessBuilder(cmd).inheritIO().start(); + System.exit(0); + } + + // ---------------- DETECTION ---------------- + + private static File findJava25() { + File jdks = new File(System.getProperty("user.home"), ".jdks"); + log("Scanning: " + jdks.getAbsolutePath()); + return scan(jdks); + } + + private static File scan(File root) { + if (root == null || !root.exists()) return null; + + File[] files = root.listFiles(); + if (files == null) return null; + + for (File f : files) { + log("Checking: " + f.getName()); + if (isJava25OrNewer(f)) { + log("Matched Java 25+: " + f.getAbsolutePath()); + return f; + } + } + + return null; + } + + // ---------------- VERSION ---------------- + + private static int getJavaMajorVersion(String version) { + try { + if (version.startsWith("1.")) return Integer.parseInt(version.split("\\.")[1]); + return Integer.parseInt(version.split("\\.")[0]); + } catch (Exception e) { + return -1; + } + } + + private static int getJavaMajorVersion(File javaHome) { + File bin = new File(javaHome, "bin/java"); + if (!bin.exists()) bin = new File(javaHome, "bin/java.exe"); + if (!bin.exists()) return -1; + + try { + Process p = new ProcessBuilder(bin.getAbsolutePath(), "-version") + .redirectErrorStream(true) + .start(); + + BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream())); + String line = r.readLine(); + + int q1 = line.indexOf('"'); + int q2 = line.indexOf('"', q1 + 1); + + String v = line.substring(q1 + 1, q2); + + if (v.startsWith("1.")) return Integer.parseInt(v.split("\\.")[1]); + return Integer.parseInt(v.split("\\.")[0]); + + } catch (Exception e) { + log("Failed reading version: " + javaHome); + return -1; + } + } + + private static boolean isJava25OrNewer(File home) { + return getJavaMajorVersion(home) >= 25; + } + + // ---------------- LOG ---------------- + + private static void log(String msg) { + System.out.println("[EOCV-SIM BOOTSTRAP] " + msg); + } +} \ No newline at end of file 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 27f1d5fd..d58c0aec 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 @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim import com.github.serivesmejia.eocvsim.pipeline.PipelineSource @@ -25,6 +25,8 @@ object Main { */ @JvmStatic fun main(args: Array) { + RuntimeVersionGuard.ensureJavaOrExit() + System.setProperty("sun.java2d.d3d", "false") System.setProperty("apple.awt.application.appearance", "system") System.setProperty("apple.awt.application.name", "EasyOpenCV Simulator") diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/RuntimeVersionGuard.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/RuntimeVersionGuard.java new file mode 100644 index 00000000..be34d86f --- /dev/null +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/RuntimeVersionGuard.java @@ -0,0 +1,81 @@ +package com.github.serivesmejia.eocvsim; + +import javax.swing.JOptionPane; +import java.awt.GraphicsEnvironment; +import java.util.Locale; + +public final class RuntimeVersionGuard { + + private static final int REQUIRED_JAVA_VERSION = 25; + + private RuntimeVersionGuard() { + } + + public static void ensureJavaOrExit() { + int currentVersion = getJavaFeatureVersion(); + + if (currentVersion == REQUIRED_JAVA_VERSION) { + return; + } + + String message = "EOCV-Sim requires Java " + REQUIRED_JAVA_VERSION + + " to run.\n\nDetected Java version: " + getDisplayJavaVersion() + + "\n\nPlease install Java " + REQUIRED_JAVA_VERSION + " and start EOCV-Sim again."; + + if (GraphicsEnvironment.isHeadless()) { + System.err.println(message); + } else { + JOptionPane.showMessageDialog( + null, + message, + "Unsupported Java version", + JOptionPane.ERROR_MESSAGE + ); + } + + System.exit(1); + } + + private static int getJavaFeatureVersion() { + String specVersion = System.getProperty("java.specification.version", "0"); + return parseFeatureVersion(specVersion); + } + + private static int parseFeatureVersion(String version) { + if (version == null) { + return 0; + } + + version = version.trim(); + if (version.isEmpty()) { + return 0; + } + + if (version.startsWith("1.")) { + version = version.substring(2); + } + + int dot = version.indexOf('.'); + int dash = version.indexOf('-'); + int end = version.length(); + + if (dot >= 0) { + end = dot; + } + if (dash >= 0 && dash < end) { + end = dash; + } + + try { + return Integer.parseInt(version.substring(0, end).toLowerCase(Locale.ROOT)); + } catch (NumberFormatException ignored) { + return 0; + } + } + + private static String getDisplayJavaVersion() { + return System.getProperty("java.version", "unknown"); + } +} + + diff --git a/build.gradle b/build.gradle index 92d8990b..c01f3234 100644 --- a/build.gradle +++ b/build.gradle @@ -78,12 +78,6 @@ allprojects { wpilibRepositories.use2027Repos() wpilibRepositories.addAllReleaseRepositories(project) - tasks.withType(Jar).configureEach { - manifest { - attributes['Main-Class'] = 'com.github.serivesmejia.eocvsim.Main' - } - } - if (env == 'dev') { String date = DateTimeFormatter.ofPattern("yyMMdd-HHmm").format(LocalDateTime.now()) String hash = findProperty('hash') From 0ae0590e62e746eb57a86fd0d65b529c5ddc8cbc Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Wed, 13 May 2026 02:00:04 -0600 Subject: [PATCH 28/40] Fix job names in workflow dependencies --- .github/workflows/build_ci.yml | 4 +- .../com/github/serivesmejia/eocvsim/Main.kt | 2 - .../eocvsim/RuntimeVersionGuard.java | 81 ------------------- 3 files changed, 2 insertions(+), 85 deletions(-) delete mode 100644 EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/RuntimeVersionGuard.java diff --git a/.github/workflows/build_ci.yml b/.github/workflows/build_ci.yml index 39ab494e..967f3d05 100644 --- a/.github/workflows/build_ci.yml +++ b/.github/workflows/build_ci.yml @@ -123,7 +123,7 @@ jobs: EOCV-Sim/build/libs/*.jar release: - needs: [test_linux, test_linux_arm, test_windows, test_mac, test_mac_intel] + needs: [build_linux, build_linux_arm, build_windows, build_mac, build_mac_intel] runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags/v') steps: @@ -213,7 +213,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} dev_release: - needs: [test_linux, test_linux_arm, test_windows, test_mac, test_mac_intel] + needs: [build_linux, build_linux_arm, build_windows, build_mac, build_mac_intel] runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref != 'refs/heads/master' && !startsWith(github.ref, 'refs/tags/v') steps: 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 d58c0aec..37f5807a 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 @@ -25,8 +25,6 @@ object Main { */ @JvmStatic fun main(args: Array) { - RuntimeVersionGuard.ensureJavaOrExit() - System.setProperty("sun.java2d.d3d", "false") System.setProperty("apple.awt.application.appearance", "system") System.setProperty("apple.awt.application.name", "EasyOpenCV Simulator") diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/RuntimeVersionGuard.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/RuntimeVersionGuard.java deleted file mode 100644 index be34d86f..00000000 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/RuntimeVersionGuard.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.github.serivesmejia.eocvsim; - -import javax.swing.JOptionPane; -import java.awt.GraphicsEnvironment; -import java.util.Locale; - -public final class RuntimeVersionGuard { - - private static final int REQUIRED_JAVA_VERSION = 25; - - private RuntimeVersionGuard() { - } - - public static void ensureJavaOrExit() { - int currentVersion = getJavaFeatureVersion(); - - if (currentVersion == REQUIRED_JAVA_VERSION) { - return; - } - - String message = "EOCV-Sim requires Java " + REQUIRED_JAVA_VERSION + - " to run.\n\nDetected Java version: " + getDisplayJavaVersion() + - "\n\nPlease install Java " + REQUIRED_JAVA_VERSION + " and start EOCV-Sim again."; - - if (GraphicsEnvironment.isHeadless()) { - System.err.println(message); - } else { - JOptionPane.showMessageDialog( - null, - message, - "Unsupported Java version", - JOptionPane.ERROR_MESSAGE - ); - } - - System.exit(1); - } - - private static int getJavaFeatureVersion() { - String specVersion = System.getProperty("java.specification.version", "0"); - return parseFeatureVersion(specVersion); - } - - private static int parseFeatureVersion(String version) { - if (version == null) { - return 0; - } - - version = version.trim(); - if (version.isEmpty()) { - return 0; - } - - if (version.startsWith("1.")) { - version = version.substring(2); - } - - int dot = version.indexOf('.'); - int dash = version.indexOf('-'); - int end = version.length(); - - if (dot >= 0) { - end = dot; - } - if (dash >= 0 && dash < end) { - end = dash; - } - - try { - return Integer.parseInt(version.substring(0, end).toLowerCase(Locale.ROOT)); - } catch (NumberFormatException ignored) { - return 0; - } - } - - private static String getDisplayJavaVersion() { - return System.getProperty("java.version", "unknown"); - } -} - - From fa6a660c443bf87a3b37650e799a6b5398aa4d2b Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Wed, 13 May 2026 10:20:56 -0600 Subject: [PATCH 29/40] Mention package platform in library loading crash dialog --- .../java/android/annotation/AnyThread.java | 5 ---- EOCV-Sim/build.gradle | 1 + .../serivesmejia/eocvsim/Bootstrap.java | 9 ++++++++ .../github/serivesmejia/eocvsim/EOCVSim.kt | 23 +++++++++++-------- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/Common/src/main/java/android/annotation/AnyThread.java b/Common/src/main/java/android/annotation/AnyThread.java index 32605c02..0778935e 100644 --- a/Common/src/main/java/android/annotation/AnyThread.java +++ b/Common/src/main/java/android/annotation/AnyThread.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2016 The Android Open Source Project * diff --git a/EOCV-Sim/build.gradle b/EOCV-Sim/build.gradle index 849fa43e..8a0ae9ec 100644 --- a/EOCV-Sim/build.gradle +++ b/EOCV-Sim/build.gradle @@ -187,6 +187,7 @@ public final class Build { public static final String standardVersionString = "$standardVersion"; public static final String buildDate = "$date"; public static final boolean isDev = ${version.contains("dev")}; + public static final String packagePlatform = "${wpilibTools.currentPlatform.platformName}"; public static final String opencvVersion = "$opencvVersion"; public static final String apriltagPluginVersion = "$apriltag_plugin_version"; diff --git a/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java b/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java index 578e6e2d..397d4465 100644 --- a/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java +++ b/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java @@ -5,6 +5,7 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.*; +import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -72,6 +73,14 @@ private static File promptUser(File detected, int currentJava) { final Object lock = new Object(); JFrame dialog = new JFrame("EOCV-Sim: Java 25 Required"); + + try { + URL icon = Bootstrap.class.getResource("/images/icon/ico_eocvsim_new_128.png"); + if (icon != null) { + dialog.setIconImage(new ImageIcon(icon).getImage()); + } + } catch (Exception ignored) { } + dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); JPanel root = new JPanel(); 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 7d7a00ce..f5e725d5 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 @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim import com.github.serivesmejia.eocvsim.config.Config @@ -184,8 +184,8 @@ class EOCVSim : KoinComponent { EOCVSimUncaughtExceptionHandler.register() - //loading native lib only once in the app runtime val loadLibrariesResult = LibraryLoader.loadLibraries() + if(!loadLibrariesResult.success) { logger.error("Exception in loadLibraries():", loadLibrariesResult.error) logger.error("The sim will exit now as it's impossible to continue without the required libraries") @@ -195,10 +195,13 @@ class EOCVSim : KoinComponent { runBlocking { launch(Dispatchers.Swing) { val crashHeader = """ - One or more of WPILib's native libraries failed to load at the earliest stage. - Ensure you're running EOCV-Sim on a supported platform, Java 25 is required. - Read the crash report below, this is not likely to be a bug on the program, - If it seems like a bug, please open an issue in GitHub with this report. + Failed to load one or more WPILib native libraries during initialization. + + This EOCV-Sim package is intended for ${Build.packagePlatform}. + Please verify that you are running the correct build. + + Additional details are available in the crash report below. + If this seems like a bug, please report it on GitHub. """.trimIndent() dialogFactory.instantiateCrashReport( From 8ef7bf4e55281b3c564a0d508a8a90c248e5a95e Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Wed, 13 May 2026 10:53:46 -0600 Subject: [PATCH 30/40] Fix license headers for third-party files --- .../java/android/annotation/AnyThread.java | 1 + .../java/android/annotation/ColorInt.java | 7 +- .../java/android/annotation/ColorLong.java | 6 +- .../java/android/annotation/HalfFloat.java | 6 +- .../main/java/android/annotation/IntDef.java | 6 +- .../java/android/annotation/IntRange.java | 7 +- .../main/java/android/annotation/NonNull.java | 6 +- .../java/android/annotation/Nullable.java | 7 +- .../main/java/android/annotation/Size.java | 7 +- .../android/annotation/SuppressAutoDoc.java | 6 +- .../java/android/annotation/SuppressLint.java | 6 +- .../java/android/annotation/SystemApi.java | 6 +- .../src/main/java/android/graphics/Color.java | 6 +- .../java/android/graphics/ColorSpace.java | 6 +- .../src/main/java/android/graphics/Rect.java | 6 +- .../main/java/android/hardware/DataSpace.java | 6 +- .../src/main/java/android/opengl/Matrix.java | 6 +- .../src/main/java/android/util/ArraySet.java | 6 +- .../java/android/util/ContainerHelpers.java | 6 +- Common/src/main/java/android/util/Half.java | 6 +- .../java/android/util/MapCollections.java | 6 +- Common/src/main/java/android/util/Size.java | 6 +- .../java/android/util/SparseIntArray.java | 6 +- .../java/androidx/annotation/ColorInt.java | 7 +- .../java/androidx/annotation/NonNull.java | 6 +- .../java/androidx/annotation/Nullable.java | 6 +- .../java/androidx/annotation/StringRes.java | 6 +- .../com/android/internal/util/ArrayUtils.java | 6 +- .../internal/util/GrowingArrayUtils.java | 6 +- .../eventloop/opmode/Autonomous.java | 6 +- .../robotcore/eventloop/opmode/Disabled.java | 6 +- .../robotcore/eventloop/opmode/TeleOp.java | 6 +- .../exception/RobotCoreException.java | 6 +- .../qualcomm/robotcore/util/ElapsedTime.java | 7 +- .../robotcore/util/MovingStatistics.java | 7 +- .../com/qualcomm/robotcore/util/Range.java | 7 +- .../qualcomm/robotcore/util/SerialNumber.java | 7 +- .../qualcomm/robotcore/util/Statistics.java | 7 +- .../main/java/libcore/util/EmptyArray.java | 6 +- Common/src/main/java/libcore/util/FP16.java | 6 +- .../ftc/robotcore/external/Const.java | 7 +- .../ftc/robotcore/external/NonConst.java | 7 +- .../ftc/robotcore/external/Predicate.java | 7 +- .../ftc/robotcore/external/Telemetry.java | 7 +- .../robotcore/external/android/util/Size.java | 6 +- .../robotcore/external/function/Consumer.java | 7 +- .../external/matrices/ColumnMajorMatrixF.java | 7 +- .../external/matrices/ColumnMatrixF.java | 7 +- .../external/matrices/DenseMatrixF.java | 7 +- .../external/matrices/GeneralMatrixF.java | 7 +- .../robotcore/external/matrices/MatrixF.java | 7 +- .../external/matrices/OpenGLMatrix.java | 7 +- .../external/matrices/RowMajorMatrixF.java | 7 +- .../external/matrices/RowMatrixF.java | 7 +- .../external/matrices/SliceMatrixF.java | 7 +- .../robotcore/external/matrices/VectorF.java | 7 +- .../external/navigation/Acceleration.java | 7 +- .../external/navigation/AngleUnit.java | 7 +- .../external/navigation/AxesOrder.java | 7 +- .../external/navigation/AxesReference.java | 7 +- .../robotcore/external/navigation/Axis.java | 7 +- .../external/navigation/DistanceUnit.java | 7 +- .../external/navigation/Orientation.java | 7 +- .../robotcore/external/navigation/Pose3D.java | 6 +- .../external/navigation/Position.java | 7 +- .../external/navigation/Quaternion.java | 7 +- .../navigation/UnnormalizedAngleUnit.java | 7 +- .../external/navigation/Velocity.java | 7 +- .../navigation/YawPitchRollAngles.java | 7 +- .../camera/calibration/CameraCalibration.java | 7 +- .../CameraCalibrationIdentity.java | 7 +- .../camera/calibration/CameraIntrinsics.java | 7 +- .../VendorProductCalibrationIdentity.java | 7 +- .../collections/EvictingBlockingQueue.java | 7 +- .../collections/MutableReference.java | 7 +- .../ftc/robotcore/internal/system/Assert.java | 7 +- .../ftc/robotcore/internal/system/Misc.java | 7 +- .../org/openftc/easyopencv/MatRecycler.java | 27 +++- .../openftc/easyopencv/OpenCvPipeline.java | 22 +++- .../eocvsim/util/CombinedRuntimeLoader.java | 1 + .../internal/opmode/EOCVSimTelemetryImpl.java | 7 +- .../internal/opmode/TelemetryInternal.java | 7 +- .../external/samples/ConceptAprilTag.java | 6 +- .../external/samples/ConceptAprilTagEasy.java | 6 +- .../samples/ConceptAprilTagLocalization.java | 6 +- .../samples/ConceptVisionColorLocator.java | 22 +++- .../samples/ConceptVisionColorSensor.java | 22 +++- .../teamcode/AprilTagDetectionPipeline.java | 26 +++- .../SkystoneDeterminationPipeline.java | 26 +++- .../ftc/teamcode/StageSwitchingPipeline.java | 120 ------------------ .../StoneOrientationAnalysisPipeline.java | 26 +++- .../eventloop/opmode/LinearOpMode.java | 7 +- .../robotcore/eventloop/opmode/OpMode.java | 7 +- .../qualcomm/robotcore/hardware/Gamepad.java | 6 +- .../robotcore/hardware/HardwareDevice.java | 7 +- .../camera/BuiltinCameraDirection.java | 38 +++++- .../camera/CameraCharacteristics.java | 7 +- .../hardware/camera/CameraControls.java | 7 +- .../external/hardware/camera/CameraName.java | 7 +- .../external/hardware/camera/WebcamName.java | 7 +- .../camera/controls/CameraControl.java | 7 +- .../camera/controls/ExposureControl.java | 7 +- .../camera/controls/FocusControl.java | 7 +- .../hardware/camera/controls/GainControl.java | 6 +- .../hardware/camera/controls/PtzControl.java | 6 +- .../camera/controls/WhiteBalanceControl.java | 6 +- .../ftc/vision/VisionPortal.java | 6 +- .../ftc/vision/VisionPortalImpl.java | 6 +- .../ftc/vision/VisionProcessor.java | 8 +- .../ftc/vision/VisionProcessorInternal.java | 6 +- .../apriltag/AprilTagCanvasAnnotator.java | 6 +- .../vision/apriltag/AprilTagDetection.java | 6 +- .../vision/apriltag/AprilTagGameDatabase.java | 6 +- .../ftc/vision/apriltag/AprilTagLibrary.java | 6 +- .../ftc/vision/apriltag/AprilTagMetadata.java | 6 +- .../ftc/vision/apriltag/AprilTagPoseFtc.java | 6 +- .../ftc/vision/apriltag/AprilTagPoseRaw.java | 6 +- .../vision/apriltag/AprilTagProcessor.java | 6 +- .../apriltag/AprilTagProcessorImpl.java | 6 +- .../ftc/vision/opencv/Circle.java | 6 +- .../opencv/ColorBlobLocatorProcessor.java | 6 +- .../ftc/vision/opencv/ColorRange.java | 6 +- .../ftc/vision/opencv/ColorSpace.java | 6 +- .../ftc/vision/opencv/ImageRegion.java | 6 +- .../opencv/PredominantColorProcessor.java | 6 +- .../opencv/PredominantColorProcessorImpl.java | 6 +- 126 files changed, 347 insertions(+), 733 deletions(-) delete mode 100644 TeamCode/src/main/java/org/firstinspires/ftc/teamcode/StageSwitchingPipeline.java diff --git a/Common/src/main/java/android/annotation/AnyThread.java b/Common/src/main/java/android/annotation/AnyThread.java index 0778935e..35020851 100644 --- a/Common/src/main/java/android/annotation/AnyThread.java +++ b/Common/src/main/java/android/annotation/AnyThread.java @@ -42,3 +42,4 @@ @Target({METHOD,CONSTRUCTOR,TYPE}) public @interface AnyThread { } + diff --git a/Common/src/main/java/android/annotation/ColorInt.java b/Common/src/main/java/android/annotation/ColorInt.java index 9f371a7a..7dd3b447 100644 --- a/Common/src/main/java/android/annotation/ColorInt.java +++ b/Common/src/main/java/android/annotation/ColorInt.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2015 The Android Open Source Project * @@ -40,3 +35,5 @@ @Target({PARAMETER,METHOD,LOCAL_VARIABLE,FIELD}) public @interface ColorInt { } + + diff --git a/Common/src/main/java/android/annotation/ColorLong.java b/Common/src/main/java/android/annotation/ColorLong.java index 1bf27efc..ed96189a 100644 --- a/Common/src/main/java/android/annotation/ColorLong.java +++ b/Common/src/main/java/android/annotation/ColorLong.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2016 The Android Open Source Project * @@ -47,3 +42,4 @@ @Target({PARAMETER,METHOD,LOCAL_VARIABLE,FIELD}) public @interface ColorLong { } + diff --git a/Common/src/main/java/android/annotation/HalfFloat.java b/Common/src/main/java/android/annotation/HalfFloat.java index 2e75b0c6..12b9de26 100644 --- a/Common/src/main/java/android/annotation/HalfFloat.java +++ b/Common/src/main/java/android/annotation/HalfFloat.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2016 The Android Open Source Project * @@ -48,3 +43,4 @@ @Target({PARAMETER, METHOD, LOCAL_VARIABLE, FIELD}) public @interface HalfFloat { } + diff --git a/Common/src/main/java/android/annotation/IntDef.java b/Common/src/main/java/android/annotation/IntDef.java index 7a081c63..97dc7105 100644 --- a/Common/src/main/java/android/annotation/IntDef.java +++ b/Common/src/main/java/android/annotation/IntDef.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2013 The Android Open Source Project * @@ -58,3 +53,4 @@ /** Defines whether the constants can be used as a flag, or just as an enum (the default) */ boolean flag() default false; } + diff --git a/Common/src/main/java/android/annotation/IntRange.java b/Common/src/main/java/android/annotation/IntRange.java index 8b93eb40..dcea7528 100644 --- a/Common/src/main/java/android/annotation/IntRange.java +++ b/Common/src/main/java/android/annotation/IntRange.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2015 The Android Open Source Project * @@ -48,3 +43,5 @@ /** Largest value, inclusive */ long to() default Long.MAX_VALUE; } + + diff --git a/Common/src/main/java/android/annotation/NonNull.java b/Common/src/main/java/android/annotation/NonNull.java index 33049d7c..2ee2b7de 100644 --- a/Common/src/main/java/android/annotation/NonNull.java +++ b/Common/src/main/java/android/annotation/NonNull.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2013 The Android Open Source Project * @@ -37,3 +32,4 @@ @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE}) public @interface NonNull { } + diff --git a/Common/src/main/java/android/annotation/Nullable.java b/Common/src/main/java/android/annotation/Nullable.java index 47dc6fd9..1c6165e8 100644 --- a/Common/src/main/java/android/annotation/Nullable.java +++ b/Common/src/main/java/android/annotation/Nullable.java @@ -1,9 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - - /* * Copyright (C) 2013 The Android Open Source Project * @@ -48,3 +42,4 @@ // @SystemApi(client = Client.MODULE_LIBRARIES) public @interface Nullable { } + diff --git a/Common/src/main/java/android/annotation/Size.java b/Common/src/main/java/android/annotation/Size.java index cfa4b3e4..bf17afcc 100644 --- a/Common/src/main/java/android/annotation/Size.java +++ b/Common/src/main/java/android/annotation/Size.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2015 The Android Open Source Project * @@ -52,3 +47,5 @@ /** The size must be a multiple of this factor */ long multiple() default 1; } + + diff --git a/Common/src/main/java/android/annotation/SuppressAutoDoc.java b/Common/src/main/java/android/annotation/SuppressAutoDoc.java index 83eddc97..9e715f48 100644 --- a/Common/src/main/java/android/annotation/SuppressAutoDoc.java +++ b/Common/src/main/java/android/annotation/SuppressAutoDoc.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2017 The Android Open Source Project * @@ -38,3 +33,4 @@ @Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) public @interface SuppressAutoDoc { } + diff --git a/Common/src/main/java/android/annotation/SuppressLint.java b/Common/src/main/java/android/annotation/SuppressLint.java index 9fd2b196..e7910de2 100644 --- a/Common/src/main/java/android/annotation/SuppressLint.java +++ b/Common/src/main/java/android/annotation/SuppressLint.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2012 The Android Open Source Project * @@ -38,3 +33,4 @@ */ String[] value(); } + diff --git a/Common/src/main/java/android/annotation/SystemApi.java b/Common/src/main/java/android/annotation/SystemApi.java index b4ce283e..beede907 100644 --- a/Common/src/main/java/android/annotation/SystemApi.java +++ b/Common/src/main/java/android/annotation/SystemApi.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2014 The Android Open Source Project * @@ -77,3 +72,4 @@ enum Client { SystemApi[] value(); } } + diff --git a/Common/src/main/java/android/graphics/Color.java b/Common/src/main/java/android/graphics/Color.java index fdbeb886..1b04aedd 100644 --- a/Common/src/main/java/android/graphics/Color.java +++ b/Common/src/main/java/android/graphics/Color.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2006 The Android Open Source Project * @@ -1486,3 +1481,4 @@ private static int nativeHSVToColor(int alpha, float hsv[]) { sColorNameMap.put("teal", 0xFF008080); } } + diff --git a/Common/src/main/java/android/graphics/ColorSpace.java b/Common/src/main/java/android/graphics/ColorSpace.java index 28b33963..8ce2e725 100644 --- a/Common/src/main/java/android/graphics/ColorSpace.java +++ b/Common/src/main/java/android/graphics/ColorSpace.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2016 The Android Open Source Project * @@ -3645,3 +3640,4 @@ public float[] transform(@NonNull @Size(min = 3) float[] v) { } } } + diff --git a/Common/src/main/java/android/graphics/Rect.java b/Common/src/main/java/android/graphics/Rect.java index 08de4465..80b9b54d 100644 --- a/Common/src/main/java/android/graphics/Rect.java +++ b/Common/src/main/java/android/graphics/Rect.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2006 The Android Open Source Project * @@ -622,3 +617,4 @@ public org.jetbrains.skia.Rect toSkijaRect() { return new org.jetbrains.skia.Rect(left, top, right, bottom); } } + diff --git a/Common/src/main/java/android/hardware/DataSpace.java b/Common/src/main/java/android/hardware/DataSpace.java index aef46610..9b7d6d8d 100644 --- a/Common/src/main/java/android/hardware/DataSpace.java +++ b/Common/src/main/java/android/hardware/DataSpace.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright 2021 The Android Open Source Project * @@ -633,3 +628,4 @@ private DataSpace() {} return range; } } + diff --git a/Common/src/main/java/android/opengl/Matrix.java b/Common/src/main/java/android/opengl/Matrix.java index f120bcfc..90feb7cc 100644 --- a/Common/src/main/java/android/opengl/Matrix.java +++ b/Common/src/main/java/android/opengl/Matrix.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2007 The Android Open Source Project * @@ -856,3 +851,4 @@ public static void setLookAtM(float[] rm, int rmOffset, translateM(rm, rmOffset, -eyeX, -eyeY, -eyeZ); } } + diff --git a/Common/src/main/java/android/util/ArraySet.java b/Common/src/main/java/android/util/ArraySet.java index 2218c650..7bcc46d3 100644 --- a/Common/src/main/java/android/util/ArraySet.java +++ b/Common/src/main/java/android/util/ArraySet.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2013 The Android Open Source Project * @@ -906,3 +901,4 @@ public boolean retainAll(Collection collection) { return removed; } } + diff --git a/Common/src/main/java/android/util/ContainerHelpers.java b/Common/src/main/java/android/util/ContainerHelpers.java index 2e8e0124..7c57d3ab 100644 --- a/Common/src/main/java/android/util/ContainerHelpers.java +++ b/Common/src/main/java/android/util/ContainerHelpers.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2013 The Android Open Source Project * @@ -54,3 +49,4 @@ static int binarySearch(long[] array, int size, long value) { return ~lo; // value not present } } + diff --git a/Common/src/main/java/android/util/Half.java b/Common/src/main/java/android/util/Half.java index c557c6f9..5cf94a51 100644 --- a/Common/src/main/java/android/util/Half.java +++ b/Common/src/main/java/android/util/Half.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2016 The Android Open Source Project * @@ -850,3 +845,4 @@ public static String toHexString(@HalfFloat short h) { return FP16.toHexString(h); } } + diff --git a/Common/src/main/java/android/util/MapCollections.java b/Common/src/main/java/android/util/MapCollections.java index b0684a4a..390f5f47 100644 --- a/Common/src/main/java/android/util/MapCollections.java +++ b/Common/src/main/java/android/util/MapCollections.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2013 The Android Open Source Project * @@ -490,3 +485,4 @@ public Collection getValues() { protected abstract void colRemoveAt(int index); protected abstract void colClear(); } + diff --git a/Common/src/main/java/android/util/Size.java b/Common/src/main/java/android/util/Size.java index 8920d094..2072054a 100644 --- a/Common/src/main/java/android/util/Size.java +++ b/Common/src/main/java/android/util/Size.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2013 The Android Open Source Project * @@ -143,3 +138,4 @@ public int hashCode() { private final int mWidth; private final int mHeight; } + diff --git a/Common/src/main/java/android/util/SparseIntArray.java b/Common/src/main/java/android/util/SparseIntArray.java index 26ff6fd9..84eb9d0d 100644 --- a/Common/src/main/java/android/util/SparseIntArray.java +++ b/Common/src/main/java/android/util/SparseIntArray.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2006 The Android Open Source Project * @@ -287,3 +282,4 @@ public String toString() { return buffer.toString(); } } + diff --git a/Common/src/main/java/androidx/annotation/ColorInt.java b/Common/src/main/java/androidx/annotation/ColorInt.java index 48acc050..056ac765 100644 --- a/Common/src/main/java/androidx/annotation/ColorInt.java +++ b/Common/src/main/java/androidx/annotation/ColorInt.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2015 The Android Open Source Project * @@ -40,3 +35,5 @@ @Target({PARAMETER,METHOD,LOCAL_VARIABLE,FIELD}) public @interface ColorInt { } + + diff --git a/Common/src/main/java/androidx/annotation/NonNull.java b/Common/src/main/java/androidx/annotation/NonNull.java index 82d533e3..f4d8d11e 100644 --- a/Common/src/main/java/androidx/annotation/NonNull.java +++ b/Common/src/main/java/androidx/annotation/NonNull.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2013 The Android Open Source Project * @@ -39,3 +34,4 @@ @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE}) public @interface NonNull { } + diff --git a/Common/src/main/java/androidx/annotation/Nullable.java b/Common/src/main/java/androidx/annotation/Nullable.java index 898a10e7..a5208a2f 100644 --- a/Common/src/main/java/androidx/annotation/Nullable.java +++ b/Common/src/main/java/androidx/annotation/Nullable.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2013 The Android Open Source Project * @@ -45,3 +40,4 @@ @Target({METHOD, PARAMETER, FIELD}) public @interface Nullable { } + diff --git a/Common/src/main/java/androidx/annotation/StringRes.java b/Common/src/main/java/androidx/annotation/StringRes.java index 9090afab..888b13ee 100644 --- a/Common/src/main/java/androidx/annotation/StringRes.java +++ b/Common/src/main/java/androidx/annotation/StringRes.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2014 The Android Open Source Project * @@ -36,3 +31,4 @@ @Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE}) public @interface StringRes { } + diff --git a/Common/src/main/java/com/android/internal/util/ArrayUtils.java b/Common/src/main/java/com/android/internal/util/ArrayUtils.java index 1ff41dda..1135a904 100644 --- a/Common/src/main/java/com/android/internal/util/ArrayUtils.java +++ b/Common/src/main/java/com/android/internal/util/ArrayUtils.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2006 The Android Open Source Project * @@ -866,3 +861,4 @@ public static List toList(T[] array) { return list; } } + diff --git a/Common/src/main/java/com/android/internal/util/GrowingArrayUtils.java b/Common/src/main/java/com/android/internal/util/GrowingArrayUtils.java index fa512b3b..956fbba4 100644 --- a/Common/src/main/java/com/android/internal/util/GrowingArrayUtils.java +++ b/Common/src/main/java/com/android/internal/util/GrowingArrayUtils.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2014 The Android Open Source Project * @@ -189,3 +184,4 @@ public static int growSize(int currentSize) { // Uninstantiable private GrowingArrayUtils() {} } + diff --git a/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/Autonomous.java b/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/Autonomous.java index 40e3e333..e633428e 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/Autonomous.java +++ b/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/Autonomous.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2015 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2015 Robert Atkinson * @@ -78,3 +73,4 @@ */ String preselectTeleOp() default ""; } + diff --git a/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/Disabled.java b/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/Disabled.java index baa93890..a741fb3d 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/Disabled.java +++ b/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/Disabled.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2015 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2015 Robert Atkinson * @@ -58,3 +53,4 @@ public @interface Disabled { } + diff --git a/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/TeleOp.java b/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/TeleOp.java index 54ead7dc..9d10c9b8 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/TeleOp.java +++ b/Common/src/main/java/com/qualcomm/robotcore/eventloop/opmode/TeleOp.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2015 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2015 Robert Atkinson * @@ -69,3 +64,4 @@ */ String group() default ""; } + diff --git a/Common/src/main/java/com/qualcomm/robotcore/exception/RobotCoreException.java b/Common/src/main/java/com/qualcomm/robotcore/exception/RobotCoreException.java index e39619a3..8e305b27 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/exception/RobotCoreException.java +++ b/Common/src/main/java/com/qualcomm/robotcore/exception/RobotCoreException.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2014 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2014, 2015 Qualcomm Technologies Inc * @@ -57,3 +52,4 @@ public static RobotCoreException createChained(Exception e, String format, Objec return new RobotCoreException(String.format(format, args), e); } } + diff --git a/Common/src/main/java/com/qualcomm/robotcore/util/ElapsedTime.java b/Common/src/main/java/com/qualcomm/robotcore/util/ElapsedTime.java index c91cd709..cad1179f 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/util/ElapsedTime.java +++ b/Common/src/main/java/com/qualcomm/robotcore/util/ElapsedTime.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2014 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2014, 2015 Qualcomm Technologies Inc All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -272,3 +267,5 @@ public enum Resolution { MILLISECONDS } } + + diff --git a/Common/src/main/java/com/qualcomm/robotcore/util/MovingStatistics.java b/Common/src/main/java/com/qualcomm/robotcore/util/MovingStatistics.java index 70b9b8ed..6d709904 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/util/MovingStatistics.java +++ b/Common/src/main/java/com/qualcomm/robotcore/util/MovingStatistics.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson All rights reserved. @@ -128,3 +123,5 @@ public void add(double x) } } } + + diff --git a/Common/src/main/java/com/qualcomm/robotcore/util/Range.java b/Common/src/main/java/com/qualcomm/robotcore/util/Range.java index 74d59c46..e82fa86c 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/util/Range.java +++ b/Common/src/main/java/com/qualcomm/robotcore/util/Range.java @@ -1,9 +1,3 @@ -/* - * Copyright (c) 2014 Sebastian Erives - * Licensed under the MIT License. - */ - - /* * Copyright (c) 2014, 2015 Qualcomm Technologies Inc * @@ -161,3 +155,4 @@ public static void throwIfRangeIsInvalid(int number, int min, int max) throws Il } } } + diff --git a/Common/src/main/java/com/qualcomm/robotcore/util/SerialNumber.java b/Common/src/main/java/com/qualcomm/robotcore/util/SerialNumber.java index 9f1bce68..07216797 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/util/SerialNumber.java +++ b/Common/src/main/java/com/qualcomm/robotcore/util/SerialNumber.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2014 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2014, 2015 Qualcomm Technologies Inc All rights reserved. @@ -110,3 +105,5 @@ public int hashCode() { } + + diff --git a/Common/src/main/java/com/qualcomm/robotcore/util/Statistics.java b/Common/src/main/java/com/qualcomm/robotcore/util/Statistics.java index 9be5e196..992c6060 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/util/Statistics.java +++ b/Common/src/main/java/com/qualcomm/robotcore/util/Statistics.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson All rights reserved. @@ -143,3 +138,5 @@ public void remove(double x) } } } + + diff --git a/Common/src/main/java/libcore/util/EmptyArray.java b/Common/src/main/java/libcore/util/EmptyArray.java index a4ecd4cc..9450fdb1 100644 --- a/Common/src/main/java/libcore/util/EmptyArray.java +++ b/Common/src/main/java/libcore/util/EmptyArray.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2010 The Android Open Source Project * @@ -75,3 +70,4 @@ private EmptyArray() {} /** @hide */ public static final Annotation[] ANNOTATION = new Annotation[0]; } + diff --git a/Common/src/main/java/libcore/util/FP16.java b/Common/src/main/java/libcore/util/FP16.java index 8452c803..7666793d 100644 --- a/Common/src/main/java/libcore/util/FP16.java +++ b/Common/src/main/java/libcore/util/FP16.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2019 The Android Open Source Project * @@ -797,3 +792,4 @@ public static String toHexString(short h) { return o.toString(); } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Const.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Const.java index b9823e38..82128efb 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Const.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Const.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -58,3 +53,5 @@ are permitted (subject to the limitations in the disclaimer below) provided that { } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/NonConst.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/NonConst.java index ad6db054..a9741cd8 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/NonConst.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/NonConst.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -58,3 +53,5 @@ are permitted (subject to the limitations in the disclaimer below) provided that { } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Predicate.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Predicate.java index 978a06b6..e3fb4a83 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Predicate.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Predicate.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2017 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2017 Robert Atkinson All rights reserved. @@ -35,3 +30,5 @@ public interface Predicate { boolean test(T t); } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Telemetry.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Telemetry.java index aead5c69..35ac4d53 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Telemetry.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Telemetry.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -636,3 +631,5 @@ enum DisplayOrder { NEWEST_FIRST, OLDEST_FIRST } Log log(); } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/android/util/Size.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/android/util/Size.java index c88e00ae..083534b0 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/android/util/Size.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/android/util/Size.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (C) 2013 The Android Open Source Project * @@ -155,3 +150,4 @@ public int hashCode() { private final int mWidth; private final int mHeight; } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/function/Consumer.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/function/Consumer.java index bfc4d1ca..99bb2145 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/function/Consumer.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/function/Consumer.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson All rights reserved. @@ -42,3 +37,5 @@ public interface Consumer { */ void accept(T value); } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/ColumnMajorMatrixF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/ColumnMajorMatrixF.java index 356622d6..e8190443 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/ColumnMajorMatrixF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/ColumnMajorMatrixF.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -60,3 +55,5 @@ public ColumnMajorMatrixF(int nRows, int nCols) } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/ColumnMatrixF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/ColumnMatrixF.java index e5cbab7c..cc675ae2 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/ColumnMatrixF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/ColumnMatrixF.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -66,3 +61,5 @@ public ColumnMatrixF(VectorF vector) } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/DenseMatrixF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/DenseMatrixF.java index 1161ce0e..09d94bab 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/DenseMatrixF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/DenseMatrixF.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -79,3 +74,5 @@ protected DenseMatrixF(int nRows, int nCols) protected abstract int indexFromRowCol(int row, int col); } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/GeneralMatrixF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/GeneralMatrixF.java index 466d336e..3494f08f 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/GeneralMatrixF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/GeneralMatrixF.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -79,3 +74,5 @@ public GeneralMatrixF transposed() } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/MatrixF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/MatrixF.java index 6bfd9edf..59dc1b1b 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/MatrixF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/MatrixF.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -807,3 +802,5 @@ protected static RuntimeException dimensionsError(int numRows, int numCols) } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/OpenGLMatrix.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/OpenGLMatrix.java index ce99460a..e5c9ed44 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/OpenGLMatrix.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/OpenGLMatrix.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -268,3 +263,5 @@ public static OpenGLMatrix identityMatrix() } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/RowMajorMatrixF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/RowMajorMatrixF.java index 27758d2c..c74ee803 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/RowMajorMatrixF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/RowMajorMatrixF.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -61,3 +56,5 @@ protected int indexFromRowCol(int row, int col) } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/RowMatrixF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/RowMatrixF.java index 0d826f49..3ac2a06f 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/RowMatrixF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/RowMatrixF.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -66,3 +61,5 @@ public RowMatrixF(VectorF vector) } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/SliceMatrixF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/SliceMatrixF.java index dafec28b..8b464d9a 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/SliceMatrixF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/SliceMatrixF.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -94,3 +89,5 @@ public SliceMatrixF(MatrixF matrix, int row, int col, int numRows, int numCols) } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/VectorF.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/VectorF.java index 98972b45..3d1d1a89 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/VectorF.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/matrices/VectorF.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -322,3 +317,5 @@ protected RuntimeException dimensionsError() } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Acceleration.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Acceleration.java index 541d4a05..63d94686 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Acceleration.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Acceleration.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -124,3 +119,5 @@ public Acceleration toUnit(DistanceUnit distanceUnit) } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AngleUnit.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AngleUnit.java index 9b288fcd..e7cb276e 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AngleUnit.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AngleUnit.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -243,3 +238,5 @@ public UnnormalizedAngleUnit getUnnormalized() } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AxesOrder.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AxesOrder.java index 6fd28c74..b2830ea0 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AxesOrder.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AxesOrder.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -106,3 +101,5 @@ public AxesOrder reverse() } } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AxesReference.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AxesReference.java index 358ecfc9..8a93b5df 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AxesReference.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/AxesReference.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -61,3 +56,5 @@ public AxesReference reverse() } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Axis.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Axis.java index 011ba401..bac1c687 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Axis.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Axis.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -63,3 +58,5 @@ public static Axis fromIndex(int index) } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/DistanceUnit.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/DistanceUnit.java index 5d6da5d8..725d7bd3 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/DistanceUnit.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/DistanceUnit.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -206,3 +201,5 @@ public String toString(double inOurUnits) } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Orientation.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Orientation.java index 3cf68727..a52af343 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Orientation.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Orientation.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -890,3 +885,5 @@ else if (test == 1) } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Pose3D.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Pose3D.java index 8fddc08b..a1dc9b7f 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Pose3D.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Pose3D.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2024 Dryw Wade * @@ -93,3 +88,4 @@ public Position getPosition() } } + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Position.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Position.java index 27ce3a42..b308e7a3 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Position.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Position.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -105,3 +100,5 @@ public Position toUnit(DistanceUnit distanceUnit) } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Quaternion.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Quaternion.java index 9187b05a..a3333e88 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Quaternion.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Quaternion.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -304,3 +299,5 @@ public String toString() } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/UnnormalizedAngleUnit.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/UnnormalizedAngleUnit.java index 2989385e..88baca5f 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/UnnormalizedAngleUnit.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/UnnormalizedAngleUnit.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -201,3 +196,5 @@ public AngleUnit getNormalized() } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Velocity.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Velocity.java index 12c1e658..6a73b589 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Velocity.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/Velocity.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -108,3 +103,5 @@ public Velocity toUnit(DistanceUnit distanceUnit) } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/YawPitchRollAngles.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/YawPitchRollAngles.java index 6c70633c..34dcec3c 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/YawPitchRollAngles.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/navigation/YawPitchRollAngles.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2022 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2022 REV Robotics @@ -148,3 +143,5 @@ public String toString() { } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraCalibration.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraCalibration.java index 8b81bce8..9e5811a8 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraCalibration.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraCalibration.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2018 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2018 Robert Atkinson @@ -178,3 +173,5 @@ protected static double getAspectRatio(Size size) } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraCalibrationIdentity.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraCalibrationIdentity.java index dad7520e..62aa3e83 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraCalibrationIdentity.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraCalibrationIdentity.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2018 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2018 Robert Atkinson @@ -41,3 +36,5 @@ public interface CameraCalibrationIdentity { boolean isDegenerate(); } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraIntrinsics.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraIntrinsics.java index bd1e6793..7e1e7d1c 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraIntrinsics.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/CameraIntrinsics.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2018 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2018 Robert Atkinson @@ -120,3 +115,5 @@ public boolean isDegenerate() } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/VendorProductCalibrationIdentity.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/VendorProductCalibrationIdentity.java index 0c3ee9c1..f558d8b6 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/VendorProductCalibrationIdentity.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/camera/calibration/VendorProductCalibrationIdentity.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2018 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2018 Robert Atkinson @@ -88,3 +83,5 @@ public VendorProductCalibrationIdentity(int vid, int pid) } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/collections/EvictingBlockingQueue.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/collections/EvictingBlockingQueue.java index 97ef2f3d..a20078a0 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/collections/EvictingBlockingQueue.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/collections/EvictingBlockingQueue.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson All rights reserved. @@ -218,3 +213,5 @@ public void clear() { } } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/collections/MutableReference.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/collections/MutableReference.java index 12393c8f..a2c53585 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/collections/MutableReference.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/collections/MutableReference.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2017 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2017 Robert Atkinson @@ -72,3 +67,5 @@ public T getValue() } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/Assert.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/Assert.java index b50e9282..02d5aa61 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/Assert.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/Assert.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson All rights reserved. @@ -122,3 +117,5 @@ public static void assertFailed(String format, Object[] args) { } } } + + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/Misc.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/Misc.java index 772b3a5e..ff38c962 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/Misc.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/Misc.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2017 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2017 Robert Atkinson @@ -478,3 +473,5 @@ public static RuntimeException internalError(Throwable throwable, String message return new RuntimeException("internal error:" + message, throwable); } } + + diff --git a/Common/src/main/java/org/openftc/easyopencv/MatRecycler.java b/Common/src/main/java/org/openftc/easyopencv/MatRecycler.java index dad3448b..e8e76329 100644 --- a/Common/src/main/java/org/openftc/easyopencv/MatRecycler.java +++ b/Common/src/main/java/org/openftc/easyopencv/MatRecycler.java @@ -1,8 +1,25 @@ -/* - * Copyright (c) 2019 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2019 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + package org.openftc.easyopencv; import org.opencv.core.CvType; diff --git a/Common/src/main/java/org/openftc/easyopencv/OpenCvPipeline.java b/Common/src/main/java/org/openftc/easyopencv/OpenCvPipeline.java index 07bb8da3..c235df5e 100644 --- a/Common/src/main/java/org/openftc/easyopencv/OpenCvPipeline.java +++ b/Common/src/main/java/org/openftc/easyopencv/OpenCvPipeline.java @@ -1,6 +1,22 @@ /* - * Copyright (c) 2019 Sebastian Erives - * Licensed under the MIT License. + * Copyright (c) 2019 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package org.openftc.easyopencv; @@ -75,4 +91,4 @@ Mat processFrameInternal(Mat input) return processFrame(input); } -} +} diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/CombinedRuntimeLoader.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/CombinedRuntimeLoader.java index 75eceeb0..56f62cc6 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/CombinedRuntimeLoader.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/CombinedRuntimeLoader.java @@ -303,3 +303,4 @@ public static void loadLibraries(Class clazz, String... librariesToLoad) } } } + diff --git a/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/EOCVSimTelemetryImpl.java b/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/EOCVSimTelemetryImpl.java index 5def86b0..683275db 100644 --- a/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/EOCVSimTelemetryImpl.java +++ b/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/EOCVSimTelemetryImpl.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson @@ -1019,3 +1014,5 @@ protected void onAddData() } } + + diff --git a/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/TelemetryInternal.java b/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/TelemetryInternal.java index e927e9f9..9267d6c0 100644 --- a/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/TelemetryInternal.java +++ b/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/TelemetryInternal.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2016 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2016 Robert Atkinson All rights reserved. @@ -39,3 +34,5 @@ public interface TelemetryInternal boolean tryUpdateIfDirty(); void resetTelemetryForOpMode(); } + + diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptAprilTag.java b/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptAprilTag.java index 0c433003..9c196949 100644 --- a/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptAprilTag.java +++ b/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptAprilTag.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2023 FIRST. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -189,3 +184,4 @@ private void telemetryAprilTag() { } // end method telemetryAprilTag() } // end class + diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptAprilTagEasy.java b/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptAprilTagEasy.java index ea1db7c6..c8905238 100644 --- a/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptAprilTagEasy.java +++ b/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptAprilTagEasy.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2023 FIRST. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -144,3 +139,4 @@ private void telemetryAprilTag() { } // end method telemetryAprilTag() } // end class + diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptAprilTagLocalization.java b/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptAprilTagLocalization.java index 4dfcb979..29ce90d1 100644 --- a/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptAprilTagLocalization.java +++ b/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptAprilTagLocalization.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2024 Dryw Wade. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, @@ -243,3 +238,4 @@ private void telemetryAprilTag() { } // end method telemetryAprilTag() } // end class + diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptVisionColorLocator.java b/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptVisionColorLocator.java index ab4e7a43..647516b8 100644 --- a/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptVisionColorLocator.java +++ b/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptVisionColorLocator.java @@ -1,6 +1,22 @@ /* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. + * Copyright (c) 2024 Phil Malone + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package org.firstinspires.ftc.robotcontroller.external.samples; @@ -172,4 +188,4 @@ public void runOpMode() sleep(50); } } -} +} diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptVisionColorSensor.java b/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptVisionColorSensor.java index 8b28fa4a..6be2bc4e 100644 --- a/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptVisionColorSensor.java +++ b/TeamCode/src/main/java/org/firstinspires/ftc/robotcontroller/external/samples/ConceptVisionColorSensor.java @@ -1,6 +1,22 @@ /* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. + * Copyright (c) 2024 Phil Malone + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package org.firstinspires.ftc.robotcontroller.external.samples; @@ -117,4 +133,4 @@ public void runOpMode() sleep(20); } } -} +} diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/AprilTagDetectionPipeline.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/AprilTagDetectionPipeline.java index 77fc2531..9f87f105 100644 --- a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/AprilTagDetectionPipeline.java +++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/AprilTagDetectionPipeline.java @@ -1,8 +1,24 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2021 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package org.firstinspires.ftc.teamcode; import com.qualcomm.robotcore.eventloop.opmode.Disabled; diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/SkystoneDeterminationPipeline.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/SkystoneDeterminationPipeline.java index 748bf7d7..66224f82 100644 --- a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/SkystoneDeterminationPipeline.java +++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/SkystoneDeterminationPipeline.java @@ -1,8 +1,24 @@ -/* - * Copyright (c) 2020 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2020 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package org.firstinspires.ftc.teamcode; import com.qualcomm.robotcore.eventloop.opmode.Disabled; diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/StageSwitchingPipeline.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/StageSwitchingPipeline.java deleted file mode 100644 index af8b34aa..00000000 --- a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/StageSwitchingPipeline.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (c) 2020 Sebastian Erives - * Licensed under the MIT License. - */ - -package org.firstinspires.ftc.teamcode; - -import com.qualcomm.robotcore.eventloop.opmode.Disabled; -import org.firstinspires.ftc.robotcore.external.Telemetry; -import org.opencv.core.Core; -import org.opencv.core.Mat; -import org.opencv.core.MatOfPoint; -import org.opencv.core.Scalar; -import org.opencv.imgproc.Imgproc; -import org.openftc.easyopencv.OpenCvPipeline; - -import java.util.ArrayList; -import java.util.List; - -@Disabled -public class StageSwitchingPipeline extends OpenCvPipeline -{ - Mat yCbCrChan2Mat = new Mat(); - Mat thresholdMat = new Mat(); - Mat contoursOnFrameMat = new Mat(); - List contoursList = new ArrayList<>(); - int numContoursFound; - - enum Stage - { - YCbCr_CHAN2, - THRESHOLD, - CONTOURS_OVERLAYED_ON_FRAME, - RAW_IMAGE, - } - - private Stage stageToRenderToViewport = Stage.YCbCr_CHAN2; - private Stage[] stages = Stage.values(); - - private Telemetry telemetry; - - public StageSwitchingPipeline(Telemetry telemetry) { - this.telemetry = telemetry; - } - - @Override - public void onViewportTapped() - { - /* - * Note that this method is invoked from the UI thread - * so whatever we do here, we must do quickly. - */ - - int currentStageNum = stageToRenderToViewport.ordinal(); - - int nextStageNum = currentStageNum + 1; - - if(nextStageNum >= stages.length) - { - nextStageNum = 0; - } - - stageToRenderToViewport = stages[nextStageNum]; - } - - @Override - public Mat processFrame(Mat input) - { - contoursList.clear(); - - /* - * This pipeline finds the contours of yellow blobs such as the Gold Mineral - * from the Rover Ruckus game. - */ - Imgproc.cvtColor(input, yCbCrChan2Mat, Imgproc.COLOR_RGB2YCrCb); - Core.extractChannel(yCbCrChan2Mat, yCbCrChan2Mat, 2); - Imgproc.threshold(yCbCrChan2Mat, thresholdMat, 102, 255, Imgproc.THRESH_BINARY_INV); - Imgproc.findContours(thresholdMat, contoursList, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE); - numContoursFound = contoursList.size(); - input.copyTo(contoursOnFrameMat); - Imgproc.drawContours(contoursOnFrameMat, contoursList, -1, new Scalar(0, 0, 255), 3, 8); - - telemetry.addData("[Stage]", stageToRenderToViewport); - telemetry.addData("[Found Contours]", "%d", numContoursFound); - telemetry.update(); - - switch (stageToRenderToViewport) - { - case YCbCr_CHAN2: - { - return yCbCrChan2Mat; - } - - case THRESHOLD: - { - return thresholdMat; - } - - case CONTOURS_OVERLAYED_ON_FRAME: - { - return contoursOnFrameMat; - } - - case RAW_IMAGE: - { - return input; - } - - default: - { - return input; - } - } - } - - public int getNumContoursFound() - { - return numContoursFound; - } -} diff --git a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/StoneOrientationAnalysisPipeline.java b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/StoneOrientationAnalysisPipeline.java index 0d95fca8..bb9f75a5 100644 --- a/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/StoneOrientationAnalysisPipeline.java +++ b/TeamCode/src/main/java/org/firstinspires/ftc/teamcode/StoneOrientationAnalysisPipeline.java @@ -1,8 +1,24 @@ -/* - * Copyright (c) 2020 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2020 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package org.firstinspires.ftc.teamcode; import com.qualcomm.robotcore.eventloop.opmode.Disabled; diff --git a/Vision/src/main/java/com/qualcomm/robotcore/eventloop/opmode/LinearOpMode.java b/Vision/src/main/java/com/qualcomm/robotcore/eventloop/opmode/LinearOpMode.java index 21fe456f..24571c5a 100644 --- a/Vision/src/main/java/com/qualcomm/robotcore/eventloop/opmode/LinearOpMode.java +++ b/Vision/src/main/java/com/qualcomm/robotcore/eventloop/opmode/LinearOpMode.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2014 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2014 Qualcomm Technologies Inc All rights reserved. @@ -237,3 +232,5 @@ public void run() { } + + diff --git a/Vision/src/main/java/com/qualcomm/robotcore/eventloop/opmode/OpMode.java b/Vision/src/main/java/com/qualcomm/robotcore/eventloop/opmode/OpMode.java index ead31945..176b34ae 100644 --- a/Vision/src/main/java/com/qualcomm/robotcore/eventloop/opmode/OpMode.java +++ b/Vision/src/main/java/com/qualcomm/robotcore/eventloop/opmode/OpMode.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2014 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2014 Qualcomm Technologies Inc All rights reserved. @@ -217,3 +212,5 @@ protected void forceStop() { } } + + diff --git a/Vision/src/main/java/com/qualcomm/robotcore/hardware/Gamepad.java b/Vision/src/main/java/com/qualcomm/robotcore/hardware/Gamepad.java index 4fa9cad2..01a5f88c 100644 --- a/Vision/src/main/java/com/qualcomm/robotcore/hardware/Gamepad.java +++ b/Vision/src/main/java/com/qualcomm/robotcore/hardware/Gamepad.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2014 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2014, 2015 Qualcomm Technologies Inc * @@ -427,4 +422,5 @@ protected void updateButtonAliases(){ ps = guide; } } + diff --git a/Vision/src/main/java/com/qualcomm/robotcore/hardware/HardwareDevice.java b/Vision/src/main/java/com/qualcomm/robotcore/hardware/HardwareDevice.java index 5550e477..2122111e 100644 --- a/Vision/src/main/java/com/qualcomm/robotcore/hardware/HardwareDevice.java +++ b/Vision/src/main/java/com/qualcomm/robotcore/hardware/HardwareDevice.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2014 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2014, 2015 Qualcomm Technologies Inc All rights reserved. @@ -86,3 +81,5 @@ enum Manufacturer { void close(); } + + diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/BuiltinCameraDirection.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/BuiltinCameraDirection.java index 1fa8940e..3374b032 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/BuiltinCameraDirection.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/BuiltinCameraDirection.java @@ -1,8 +1,35 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - +/* +Copyright (c) 2017 Robert Atkinson + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted (subject to the limitations in the disclaimer below) provided that +the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the name of Robert Atkinson nor the names of his contributors may be used to +endorse or promote products derived from this software without specific prior +written permission. + +NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS +LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ package org.firstinspires.ftc.robotcore.external.hardware.camera; public enum BuiltinCameraDirection @@ -10,3 +37,4 @@ public enum BuiltinCameraDirection BACK, FRONT } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraCharacteristics.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraCharacteristics.java index 3216dc40..d893750d 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraCharacteristics.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraCharacteristics.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2017 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2017 Robert Atkinson @@ -162,3 +157,5 @@ public CameraMode(int androidFormat, Size size, long nsFrameDuration, boolean is */ List getAllCameraModes(); } + + diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraControls.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraControls.java index 529a1f54..471d62e8 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraControls.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraControls.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2017 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2017 Robert Atkinson @@ -49,3 +44,5 @@ public interface CameraControls @Nullable T getControl(Class controlType); } + + diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraName.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraName.java index e3ab3546..a0a526f6 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraName.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/CameraName.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2017 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2017 Robert Atkinson @@ -87,3 +82,5 @@ public interface CameraName */ CameraCharacteristics getCameraCharacteristics(); } + + diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/WebcamName.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/WebcamName.java index bbe43bec..00a831db 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/WebcamName.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/WebcamName.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2018 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2018 Robert Atkinson @@ -66,3 +61,5 @@ public interface WebcamName extends CameraName, HardwareDevice */ boolean isAttached(); } + + diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/CameraControl.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/CameraControl.java index 453ad194..bbaeea0d 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/CameraControl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/CameraControl.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2018 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2018 Robert Atkinson @@ -45,3 +40,5 @@ are permitted (subject to the limitations in the disclaimer below) provided that public interface CameraControl { } + + diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/ExposureControl.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/ExposureControl.java index a4cc5430..75069ce8 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/ExposureControl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/ExposureControl.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2018 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2018 Robert Atkinson @@ -146,3 +141,5 @@ public static Mode fromId(int id) boolean setAePriority(boolean priority); } + + diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/FocusControl.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/FocusControl.java index 0b839413..1a2c6e36 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/FocusControl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/FocusControl.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2018 Sebastian Erives - * Licensed under the MIT License. - */ - /* Copyright (c) 2018 Robert Atkinson @@ -121,3 +116,5 @@ public static FocusControl.Mode fromId(int index) boolean isFocusLengthSupported(); } + + diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/GainControl.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/GainControl.java index f3187c98..f73f0131 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/GainControl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/GainControl.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2020 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2020 Michael Hoogasian * @@ -72,3 +67,4 @@ public interface GainControl extends CameraControl boolean setGain(int gain); } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/PtzControl.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/PtzControl.java index b4bf1fb1..d70717a5 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/PtzControl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/PtzControl.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2020 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2020 Michael Hoogasian * @@ -105,3 +100,4 @@ class PanTiltHolder */ int getMaxZoom(); } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/WhiteBalanceControl.java b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/WhiteBalanceControl.java index 24d4aa04..d46e580c 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/WhiteBalanceControl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/robotcore/external/hardware/camera/controls/WhiteBalanceControl.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2021 Michael Hoogasian * @@ -90,3 +85,4 @@ enum Mode /* If you change this order, remember to update jni_devicehandle.cpp! boolean setWhiteBalanceTemperature(int temperature); } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortal.java b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortal.java index 9c38dc89..513314b5 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortal.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortal.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2023 FIRST * @@ -476,3 +471,4 @@ public enum CameraState */ public abstract void close(); } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortalImpl.java b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortalImpl.java index 3ffb06d1..e751b687 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortalImpl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortalImpl.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2023 FIRST * @@ -422,3 +417,4 @@ public void close() } } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionProcessor.java b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionProcessor.java index e1eac933..56ffda87 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionProcessor.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionProcessor.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2023 FIRST * @@ -41,5 +36,4 @@ /** * May be attached to a {@link VisionPortal} to run image processing */ -public interface VisionProcessor extends VisionProcessorInternal {} - +public interface VisionProcessor extends VisionProcessorInternal {} \ No newline at end of file diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionProcessorInternal.java b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionProcessorInternal.java index 32db8243..10fcbee9 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionProcessorInternal.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionProcessorInternal.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2023 FIRST * @@ -52,3 +47,4 @@ interface VisionProcessorInternal Object processFrame(Mat frame, long captureTimeNanos); void onDrawFrame(Canvas canvas, int onscreenWidth, int onscreenHeight, float scaleBmpPxToCanvasPx, float scaleCanvasDensity, Object userContext); } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagCanvasAnnotator.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagCanvasAnnotator.java index 8d547c47..8d4770c6 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagCanvasAnnotator.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagCanvasAnnotator.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2023 FIRST * @@ -298,3 +293,4 @@ void drawTagID(AprilTagDetection detection, Canvas canvas) } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagDetection.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagDetection.java index ace41dc6..464179d5 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagDetection.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagDetection.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2023 FIRST * @@ -110,3 +105,4 @@ public AprilTagDetection(int id, int hamming, float decisionMargin, Point center } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagGameDatabase.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagGameDatabase.java index aad2ea8b..ec475cf2 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagGameDatabase.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagGameDatabase.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2023 FIRST * @@ -164,3 +159,4 @@ public static AprilTagLibrary getSampleTagLibrary() } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagLibrary.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagLibrary.java index 7cbf3565..fcc645c6 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagLibrary.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagLibrary.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2023 FIRST * @@ -192,3 +187,4 @@ public AprilTagLibrary build() } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagMetadata.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagMetadata.java index e3621c13..94ce790e 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagMetadata.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagMetadata.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2023 FIRST * @@ -88,3 +83,4 @@ public AprilTagMetadata(int id, String name, double tagsize, DistanceUnit distan } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagPoseFtc.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagPoseFtc.java index c769005a..008b3f97 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagPoseFtc.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagPoseFtc.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2023 FIRST * @@ -119,3 +114,4 @@ public AprilTagPoseFtc(double x, double y, double z, double yaw, double pitch, this.elevation = elevation; } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagPoseRaw.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagPoseRaw.java index b8b73268..101ff46b 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagPoseRaw.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagPoseRaw.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2023 FIRST * @@ -70,3 +65,4 @@ public AprilTagPoseRaw(double x, double y, double z, MatrixF R) this.R = R; } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagProcessor.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagProcessor.java index 65d63c23..0404e80d 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagProcessor.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagProcessor.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2023 FIRST * @@ -331,3 +326,4 @@ public enum PoseSolver } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagProcessorImpl.java b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagProcessorImpl.java index 6258fe2a..ab360334 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagProcessorImpl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/apriltag/AprilTagProcessorImpl.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2023 FIRST * @@ -614,3 +609,4 @@ public Pose(Mat rvec, Mat tvec) } } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/Circle.java b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/Circle.java index 185e2b0c..bcaeac4c 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/Circle.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/Circle.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2025 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2025 Miriam Sinton-Remes * @@ -103,3 +98,4 @@ public float getY() { } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorBlobLocatorProcessor.java b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorBlobLocatorProcessor.java index 3df83c69..6d8420ea 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorBlobLocatorProcessor.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorBlobLocatorProcessor.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2024 FIRST * @@ -648,3 +643,4 @@ public int compare(Blob c1, Blob c2) } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorRange.java b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorRange.java index b921f3f9..c741f79f 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorRange.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorRange.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2024 FIRST * @@ -102,3 +97,4 @@ public ColorRange(ColorSpace colorSpace, Scalar min, Scalar max) } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorSpace.java b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorSpace.java index b2cf1fcd..38adaf56 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorSpace.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorSpace.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2024 FIRST * @@ -48,3 +43,4 @@ public enum ColorSpace RGB, } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ImageRegion.java b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ImageRegion.java index b33a9760..11b6fc01 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ImageRegion.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ImageRegion.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2024 FIRST * @@ -164,3 +159,4 @@ protected Rect asOpenCvRect(int imageWidth, int imageHeight) } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/PredominantColorProcessor.java b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/PredominantColorProcessor.java index 7d427903..229835da 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/PredominantColorProcessor.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/PredominantColorProcessor.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2024 FIRST * @@ -174,3 +169,4 @@ public static Swatch valueOf(int swatch) } } + diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/PredominantColorProcessorImpl.java b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/PredominantColorProcessorImpl.java index 2db9d546..c78904fd 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/PredominantColorProcessorImpl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/PredominantColorProcessorImpl.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright (c) 2024 FIRST * @@ -266,3 +261,4 @@ byte[] yCrCb2Rgb(byte[] yCrCb) } } + From c7bb09a2ebbb84cc1de5d52d79af486c09c55cbb Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Wed, 13 May 2026 11:20:56 -0600 Subject: [PATCH 31/40] Fix license header of android files --- .../main/java/android/graphics/Bitmap.java | 17 ++++++++- .../main/java/android/graphics/Canvas.java | 16 +++++++- .../src/main/java/android/graphics/Paint.java | 16 +++++++- .../src/main/java/android/graphics/Path.java | 16 +++++++- .../android/graphics/TemporaryBuffer.java | 19 ++++++++-- .../main/java/android/graphics/Typeface.java | 16 +++++++- .../main/java/android/hardware/Camera.java | 21 +++++++--- .../com/formdev/flatlaf/demo/HintManager.java | 5 --- .../com/qualcomm/robotcore/util/RobotLog.java | 35 ++++++++++++++--- .../qualcomm/robotcore/util/SortOrder.java | 23 +++++++++-- .../ftc/robotcore/external/Func.java | 38 ++++++++++++++++--- .../robotcore/internal/system/AppUtil.java | 38 ++++++++++++++++--- .../serivesmejia/eocvsim/Bootstrap.java | 22 ++++++++--- .../component/visualizer/TelemetryPanel.kt | 12 +++--- .../internal/opmode/EOCVSimTelemetryImpl.java | 8 ++-- .../opmode/TelemetryTransmissionReceiver.kt | 12 +++--- .../opencv/ColorBlobLocatorProcessorImpl.java | 7 +--- .../main/java/org/opencv/android/Utils.java | 5 --- .../org/openftc/easyopencv/OpenCvCamera.java | 26 ++++++++++--- .../openftc/easyopencv/OpenCvCameraBase.java | 26 ++++++++++--- .../easyopencv/OpenCvCameraException.java | 26 ++++++++++--- .../easyopencv/OpenCvCameraFactory.java | 22 +++++++++-- .../easyopencv/OpenCvCameraRotation.java | 26 ++++++++++--- .../easyopencv/OpenCvInternalCamera.java | 27 ++++++++++--- .../easyopencv/OpenCvInternalCamera2.java | 26 ++++++++++--- .../org/openftc/easyopencv/OpenCvTracker.java | 26 ++++++++++--- .../easyopencv/OpenCvTrackerApiPipeline.java | 26 ++++++++++--- .../easyopencv/OpenCvViewRenderer.java | 23 +++++++++-- .../openftc/easyopencv/OpenCvViewport.java | 26 ++++++++++--- .../org/openftc/easyopencv/OpenCvWebcam.java | 26 ++++++++++--- .../PipelineRecordingParameters.java | 26 ++++++++++--- .../java/org/openftc/easyopencv/Util.java | 26 ++++++++++--- 32 files changed, 548 insertions(+), 136 deletions(-) diff --git a/Common/src/main/java/android/graphics/Bitmap.java b/Common/src/main/java/android/graphics/Bitmap.java index c3bfa299..8457e2bc 100644 --- a/Common/src/main/java/android/graphics/Bitmap.java +++ b/Common/src/main/java/android/graphics/Bitmap.java @@ -1,6 +1,20 @@ /* * Copyright (c) 2023 Sebastian Erives * Licensed under the MIT License. + * + * Portions of this file are derived from the Android Open Source Project, + * Copyright (C) 2006 The Android Open Source Project + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package android.graphics; @@ -225,5 +239,4 @@ public Config getConfig() { public void recycle() { theBitmap.close(); } -} - +} \ No newline at end of file diff --git a/Common/src/main/java/android/graphics/Canvas.java b/Common/src/main/java/android/graphics/Canvas.java index d4c35581..8a19391c 100644 --- a/Common/src/main/java/android/graphics/Canvas.java +++ b/Common/src/main/java/android/graphics/Canvas.java @@ -1,6 +1,20 @@ /* * Copyright (c) 2023 Sebastian Erives * Licensed under the MIT License. + * + * Portions of this file are derived from the Android Open Source Project, + * Copyright (C) 2006 The Android Open Source Project + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package android.graphics; @@ -198,4 +212,4 @@ public int getHeight() { return providedHeight; } -} +} diff --git a/Common/src/main/java/android/graphics/Paint.java b/Common/src/main/java/android/graphics/Paint.java index 2148b797..6c163daa 100644 --- a/Common/src/main/java/android/graphics/Paint.java +++ b/Common/src/main/java/android/graphics/Paint.java @@ -1,6 +1,20 @@ /* * Copyright (c) 2023 Sebastian Erives * Licensed under the MIT License. + * + * Portions of this file are derived from the Android Open Source Project, + * Copyright (C) 2006 The Android Open Source Project + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package android.graphics; @@ -349,4 +363,4 @@ public float getTextSize() { } } - + diff --git a/Common/src/main/java/android/graphics/Path.java b/Common/src/main/java/android/graphics/Path.java index 61e709e5..8dfd4bd5 100644 --- a/Common/src/main/java/android/graphics/Path.java +++ b/Common/src/main/java/android/graphics/Path.java @@ -1,6 +1,20 @@ /* * Copyright (c) 2023 Sebastian Erives * Licensed under the MIT License. + * + * Portions of this file are derived from the Android Open Source Project, + * Copyright (C) 2006 The Android Open Source Project + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package android.graphics; @@ -202,4 +216,4 @@ public void addPath(Path path) { } } - + diff --git a/Common/src/main/java/android/graphics/TemporaryBuffer.java b/Common/src/main/java/android/graphics/TemporaryBuffer.java index 83d05938..352a04a0 100644 --- a/Common/src/main/java/android/graphics/TemporaryBuffer.java +++ b/Common/src/main/java/android/graphics/TemporaryBuffer.java @@ -1,6 +1,17 @@ /* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package android.graphics; @@ -22,11 +33,13 @@ public static char[] obtain(int len) { } return buf; } + public static void recycle(char[] temp) { if (temp.length > 1000) return; synchronized (TemporaryBuffer.class) { sTemp = temp; } } + private static char[] sTemp = null; -} +} diff --git a/Common/src/main/java/android/graphics/Typeface.java b/Common/src/main/java/android/graphics/Typeface.java index 36f7990e..22e5a23a 100644 --- a/Common/src/main/java/android/graphics/Typeface.java +++ b/Common/src/main/java/android/graphics/Typeface.java @@ -1,6 +1,20 @@ /* * Copyright (c) 2023 Sebastian Erives * Licensed under the MIT License. + * + * Portions of this file are derived from the Android Open Source Project, + * Copyright (C) 2006 The Android Open Source Project + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package android.graphics; @@ -53,4 +67,4 @@ private static Data loadDataFromResource(String resource) { } } - + diff --git a/Common/src/main/java/android/hardware/Camera.java b/Common/src/main/java/android/hardware/Camera.java index 9c841220..7b88bb4e 100644 --- a/Common/src/main/java/android/hardware/Camera.java +++ b/Common/src/main/java/android/hardware/Camera.java @@ -1,8 +1,19 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package android.hardware; public class Camera { diff --git a/Common/src/main/java/com/formdev/flatlaf/demo/HintManager.java b/Common/src/main/java/com/formdev/flatlaf/demo/HintManager.java index 8229475b..d278ceda 100644 --- a/Common/src/main/java/com/formdev/flatlaf/demo/HintManager.java +++ b/Common/src/main/java/com/formdev/flatlaf/demo/HintManager.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - /* * Copyright 2020 FormDev Software GmbH * diff --git a/Common/src/main/java/com/qualcomm/robotcore/util/RobotLog.java b/Common/src/main/java/com/qualcomm/robotcore/util/RobotLog.java index dd25742d..aad21c83 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/util/RobotLog.java +++ b/Common/src/main/java/com/qualcomm/robotcore/util/RobotLog.java @@ -1,8 +1,33 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2014, 2015 Qualcomm Technologies Inc + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * (subject to the limitations in the disclaimer below) provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions + * and the following disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of Qualcomm Technologies Inc nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS + * SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + package com.qualcomm.robotcore.util; import org.slf4j.Logger; diff --git a/Common/src/main/java/com/qualcomm/robotcore/util/SortOrder.java b/Common/src/main/java/com/qualcomm/robotcore/util/SortOrder.java index 3ea3d851..1ef868ad 100644 --- a/Common/src/main/java/com/qualcomm/robotcore/util/SortOrder.java +++ b/Common/src/main/java/com/qualcomm/robotcore/util/SortOrder.java @@ -1,8 +1,25 @@ /* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. + * Copyright (c) 2024 FIRST + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + package com.qualcomm.robotcore.util; public enum SortOrder @@ -10,4 +27,4 @@ public enum SortOrder ASCENDING, DESCENDING } - + diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Func.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Func.java index 03cff183..988b1bde 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Func.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/external/Func.java @@ -1,8 +1,36 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - +/* +Copyright (c) 2016 Robert Atkinson + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted (subject to the limitations in the disclaimer below) provided that +the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the name of Robert Atkinson nor the names of his contributors may be used to +endorse or promote products derived from this software without specific prior +written permission. + +NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS +LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + package org.firstinspires.ftc.robotcore.external; public interface Func { diff --git a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/AppUtil.java b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/AppUtil.java index 65a19108..6b7417b8 100644 --- a/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/AppUtil.java +++ b/Common/src/main/java/org/firstinspires/ftc/robotcore/internal/system/AppUtil.java @@ -1,8 +1,36 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - +/* +Copyright (c) 2016 Robert Atkinson + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted (subject to the limitations in the disclaimer below) provided that +the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +Neither the name of Robert Atkinson nor the names of his contributors may be used to +endorse or promote products derived from this software without specific prior +written permission. + +NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS +LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR +TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + package org.firstinspires.ftc.robotcore.internal.system; import com.qualcomm.robotcore.util.RobotLog; diff --git a/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java b/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java index 397d4465..f1b1bdfb 100644 --- a/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java +++ b/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java @@ -1,3 +1,11 @@ +/* + * Copyright (c) 2026 Sebastian Erives, deltacv + * + * Use of this source code is governed by the MIT license + * that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT. + */ + package com.github.serivesmejia.eocvsim; import javax.swing.*; @@ -80,7 +88,7 @@ private static File promptUser(File detected, int currentJava) { dialog.setIconImage(new ImageIcon(icon).getImage()); } } catch (Exception ignored) { } - + dialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); JPanel root = new JPanel(); @@ -122,6 +130,7 @@ private static File promptUser(File detected, int currentJava) { detectedLabel.setOpaque(false); detectedLabel.setFocusable(false); detectedLabel.setFont(info.getFont().deriveFont(Font.BOLD)); + detectedLabel.setAlignmentX(Component.CENTER_ALIGNMENT); detectedPath = new JTextArea(detected.getAbsolutePath()); detectedPath.setEditable(false); @@ -129,6 +138,9 @@ private static File promptUser(File detected, int currentJava) { detectedPath.setFocusable(false); detectedPath.setFont(info.getFont().deriveFont(Font.BOLD)); detectedPath.setForeground(new Color(0, 120, 215)); + detectedPath.setAlignmentX(Component.CENTER_ALIGNMENT); + detectedPath.setLineWrap(true); + detectedPath.setWrapStyleWord(true); } // ---------------- BUTTONS ---------------- @@ -139,8 +151,8 @@ private static File promptUser(File detected, int currentJava) { continueBtn.setEnabled(hasDetected); continueBtn.setPreferredSize(new Dimension(180, 30)); - selectBtn.setPreferredSize(new Dimension(160, 30)); - exitBtn.setPreferredSize(new Dimension(360, 30)); + selectBtn.setPreferredSize(new Dimension(180, 30)); + exitBtn.setPreferredSize(new Dimension(180, 30)); // ---------------- ACTIONS ---------------- continueBtn.addActionListener(e -> { @@ -206,11 +218,11 @@ public void windowClosed(WindowEvent e) { root.add(Box.createVerticalStrut(12)); JPanel row1 = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0)); - row1.add(continueBtn); row1.add(selectBtn); + row1.add(exitBtn); JPanel row2 = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 8)); - row2.add(exitBtn); + row2.add(continueBtn); JPanel buttonBlock = new JPanel(); buttonBlock.setLayout(new BoxLayout(buttonBlock, BoxLayout.Y_AXIS)); diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/TelemetryPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/TelemetryPanel.kt index 38e94503..32c90aa3 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/TelemetryPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/TelemetryPanel.kt @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.visualizer import com.github.serivesmejia.eocvsim.pipeline.PipelineManager @@ -126,7 +126,7 @@ class TelemetryPanel : JPanel(), TelemetryTransmissionReceiver, KoinComponent { private var lastTelemetry = ""; - override fun onTelemetryTransmission(text: String, srcTelemetry: Telemetry) { + override fun consumeTelemetry(text: String, srcTelemetry: Telemetry) { SwingUtilities.invokeLater { if(lastTelemetry != text) { updateTelemetry(text, srcTelemetry.captionValueSeparator, srcTelemetry.itemSeparator) diff --git a/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/EOCVSimTelemetryImpl.java b/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/EOCVSimTelemetryImpl.java index 683275db..e5c79b24 100644 --- a/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/EOCVSimTelemetryImpl.java +++ b/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/EOCVSimTelemetryImpl.java @@ -803,7 +803,7 @@ protected void saveToTransmitter(boolean recompose) String string = currentSb.toString().trim(); for(TelemetryTransmissionReceiver receiver : transmissionReceivers) { - receiver.onTelemetryTransmission(string, this); + receiver.consumeTelemetry(string, this); } } @@ -1013,6 +1013,6 @@ protected void onAddData() // no-op } } - - - + + + diff --git a/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/TelemetryTransmissionReceiver.kt b/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/TelemetryTransmissionReceiver.kt index e281d862..9bc03507 100644 --- a/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/TelemetryTransmissionReceiver.kt +++ b/EOCV-Sim/src/main/java/org/firstinspires/ftc/robotcore/internal/opmode/TelemetryTransmissionReceiver.kt @@ -1,12 +1,12 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package org.firstinspires.ftc.robotcore.internal.opmode import org.firstinspires.ftc.robotcore.external.Telemetry interface TelemetryTransmissionReceiver { - fun onTelemetryTransmission(text: String, srcTelemetry: Telemetry) + fun consumeTelemetry(text: String, srcTelemetry: Telemetry) } diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorBlobLocatorProcessorImpl.java b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorBlobLocatorProcessorImpl.java index e37305a1..d4eb1ef1 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorBlobLocatorProcessorImpl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/opencv/ColorBlobLocatorProcessorImpl.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - package org.firstinspires.ftc.vision.opencv; import android.graphics.Canvas; @@ -519,4 +514,4 @@ public Circle getCircle() } } } - + diff --git a/Vision/src/main/java/org/opencv/android/Utils.java b/Vision/src/main/java/org/opencv/android/Utils.java index 0a57a5bf..7993773b 100644 --- a/Vision/src/main/java/org/opencv/android/Utils.java +++ b/Vision/src/main/java/org/opencv/android/Utils.java @@ -1,8 +1,3 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - package org.opencv.android; import android.graphics.Bitmap; diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCamera.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCamera.java index 843e040c..609226c7 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCamera.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCamera.java @@ -1,8 +1,24 @@ -/* - * Copyright (c) 2019 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2019 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package org.openftc.easyopencv; public interface OpenCvCamera diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraBase.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraBase.java index 03462dac..b080e5aa 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraBase.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraBase.java @@ -1,8 +1,24 @@ -/* - * Copyright (c) 2019 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2019 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package org.openftc.easyopencv; import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator; diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraException.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraException.java index eddfe1b3..2d502ed2 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraException.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraException.java @@ -1,8 +1,24 @@ -/* - * Copyright (c) 2019 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2019 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package org.openftc.easyopencv; public class OpenCvCameraException extends RuntimeException diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraFactory.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraFactory.java index 4e43e4ac..3f11ca3a 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraFactory.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraFactory.java @@ -1,6 +1,22 @@ /* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. + * Copyright (c) 2019 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ package org.openftc.easyopencv; @@ -40,4 +56,4 @@ public enum ViewportSplitMethod } } - + diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraRotation.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraRotation.java index e45892fc..5bc4d400 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraRotation.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraRotation.java @@ -1,8 +1,24 @@ -/* - * Copyright (c) 2019 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2019 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package org.openftc.easyopencv; public enum OpenCvCameraRotation diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvInternalCamera.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvInternalCamera.java index 644e2e8c..abf54c62 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvInternalCamera.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvInternalCamera.java @@ -1,8 +1,25 @@ -/* - * Copyright (c) 2019 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2019 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + package org.openftc.easyopencv; import android.hardware.Camera; diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvInternalCamera2.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvInternalCamera2.java index 9a50515c..12c5a7a0 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvInternalCamera2.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvInternalCamera2.java @@ -1,8 +1,24 @@ -/* - * Copyright (c) 2019 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2020 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package org.openftc.easyopencv; import android.hardware.Camera; diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvTracker.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvTracker.java index c66ed9f2..0277df8b 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvTracker.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvTracker.java @@ -1,8 +1,24 @@ -/* - * Copyright (c) 2019 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2019 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package org.openftc.easyopencv; import org.opencv.core.Mat; diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvTrackerApiPipeline.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvTrackerApiPipeline.java index f9936aac..1f3fd672 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvTrackerApiPipeline.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvTrackerApiPipeline.java @@ -1,8 +1,24 @@ -/* - * Copyright (c) 2019 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2019 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package org.openftc.easyopencv; import com.qualcomm.robotcore.eventloop.opmode.Disabled; diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvViewRenderer.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvViewRenderer.java index 58ede68d..feeaae5f 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvViewRenderer.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvViewRenderer.java @@ -1,8 +1,23 @@ /* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. + * Copyright (c) 2023 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ - package org.openftc.easyopencv; import android.graphics.Bitmap; @@ -320,4 +335,4 @@ public void renderPaused(Canvas canvas) canvas.drawText("VIEWPORT PAUSED", statBoxLTxtStart, textLine2Y, fpsMeterTextPaint); //canvas.drawText("Hi", statBoxLTxtStart, textLine3Y, fpsMeterTextPaint); } -} +} diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvViewport.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvViewport.java index 761126fd..103b5976 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvViewport.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvViewport.java @@ -1,8 +1,24 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2023 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package org.openftc.easyopencv; import android.graphics.Canvas; diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvWebcam.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvWebcam.java index c342bd5e..2d040068 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvWebcam.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvWebcam.java @@ -1,8 +1,24 @@ -/* - * Copyright (c) 2020 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2020 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package org.openftc.easyopencv; import org.firstinspires.ftc.robotcore.external.hardware.camera.controls.CameraControl; diff --git a/Vision/src/main/java/org/openftc/easyopencv/PipelineRecordingParameters.java b/Vision/src/main/java/org/openftc/easyopencv/PipelineRecordingParameters.java index 40e02505..01f07ecc 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/PipelineRecordingParameters.java +++ b/Vision/src/main/java/org/openftc/easyopencv/PipelineRecordingParameters.java @@ -1,8 +1,24 @@ -/* - * Copyright (c) 2020 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2020 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package org.openftc.easyopencv; import java.text.SimpleDateFormat; diff --git a/Vision/src/main/java/org/openftc/easyopencv/Util.java b/Vision/src/main/java/org/openftc/easyopencv/Util.java index d0cc1d12..802b30c3 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/Util.java +++ b/Vision/src/main/java/org/openftc/easyopencv/Util.java @@ -1,8 +1,24 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2020 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + package org.openftc.easyopencv; import java.util.concurrent.CountDownLatch; From cd1573a1ef84ff2d3065844b540f6a367fa66de1 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Wed, 13 May 2026 12:04:30 -0600 Subject: [PATCH 32/40] Improve Bootstrap prompt UI --- .../serivesmejia/eocvsim/Bootstrap.java | 61 +++++++++++-------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java b/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java index f1b1bdfb..50a23fff 100644 --- a/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java +++ b/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java @@ -71,7 +71,13 @@ public static void main(String[] args) throws Exception { // ---------------- UI ---------------- - private static File promptUser(File detected, int currentJava) { + public static final class PromptTest { + public static void main(String[] args) { + promptUser(new File("C:/Program Files/Java/jdk-25"), 17); + } + } + + static File promptUser(File detected, int currentJava) { boolean hasDetected = detected != null; @@ -112,7 +118,7 @@ private static File promptUser(File detected, int currentJava) { StringBuilder text = new StringBuilder(); text.append("EOCV-Sim was started with Java ") .append(currentJava) - .append(", but requires Java 25 or newer to run.\n\n"); + .append(", but requires Java 25 or newer to run.\n"); if (!hasDetected) { text.append("No compatible Java installation was found automatically.\n\n"); } @@ -121,26 +127,20 @@ private static File promptUser(File detected, int currentJava) { info.setMaximumSize(new Dimension(420, 120)); // ---------------- DETECTED AREAS ---------------- - JTextArea detectedLabel = null; - JTextArea detectedPath = null; + JLabel detectedLabel = null; + JLabel detectedPath = null; if (hasDetected) { - detectedLabel = new JTextArea("Autodetected installation:"); - detectedLabel.setEditable(false); - detectedLabel.setOpaque(false); - detectedLabel.setFocusable(false); + detectedLabel = new JLabel("Autodetected installation:"); detectedLabel.setFont(info.getFont().deriveFont(Font.BOLD)); detectedLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + detectedLabel.setHorizontalAlignment(SwingConstants.CENTER); - detectedPath = new JTextArea(detected.getAbsolutePath()); - detectedPath.setEditable(false); - detectedPath.setOpaque(false); - detectedPath.setFocusable(false); + detectedPath = new JLabel("
" + detected.getAbsolutePath() + "
"); detectedPath.setFont(info.getFont().deriveFont(Font.BOLD)); detectedPath.setForeground(new Color(0, 120, 215)); detectedPath.setAlignmentX(Component.CENTER_ALIGNMENT); - detectedPath.setLineWrap(true); - detectedPath.setWrapStyleWord(true); + detectedPath.setHorizontalAlignment(SwingConstants.CENTER); } // ---------------- BUTTONS ---------------- @@ -217,17 +217,28 @@ public void windowClosed(WindowEvent e) { root.add(Box.createVerticalStrut(12)); - JPanel row1 = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0)); - row1.add(selectBtn); - row1.add(exitBtn); - - JPanel row2 = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 8)); - row2.add(continueBtn); - - JPanel buttonBlock = new JPanel(); - buttonBlock.setLayout(new BoxLayout(buttonBlock, BoxLayout.Y_AXIS)); - buttonBlock.add(row1); - buttonBlock.add(row2); + JPanel buttonBlock = new JPanel(new GridBagLayout()); + buttonBlock.setBorder(BorderFactory.createEmptyBorder(0, 8, 0, 8)); + buttonBlock.setAlignmentX(Component.CENTER_ALIGNMENT); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.insets = new Insets(0, 8, 8, 8); + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.weightx = 1.0; + + // Row 0: Select and Exit + gbc.gridx = 0; + gbc.gridy = 0; + buttonBlock.add(selectBtn, gbc); + + gbc.gridx = 1; + buttonBlock.add(exitBtn, gbc); + + // Row 1: Continue with detected (spans both columns, centered) + gbc.gridx = 0; + gbc.gridy = 1; + gbc.gridwidth = 2; + gbc.anchor = GridBagConstraints.CENTER; + buttonBlock.add(continueBtn, gbc); root.add(buttonBlock); From 5527adaef48f761cae1715e0873a9db05f7cb51e Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Wed, 13 May 2026 12:18:07 -0600 Subject: [PATCH 33/40] Add ProgramFiles Java scanning on Bootstrap --- .../serivesmejia/eocvsim/Bootstrap.java | 41 +++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java b/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java index 50a23fff..6d04681e 100644 --- a/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java +++ b/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java @@ -303,9 +303,37 @@ private static void relaunch(File javaHome, String[] args) throws IOException { // ---------------- DETECTION ---------------- private static File findJava25() { - File jdks = new File(System.getProperty("user.home"), ".jdks"); - log("Scanning: " + jdks.getAbsolutePath()); - return scan(jdks); + List roots = new ArrayList<>(); + + roots.add(new File(System.getProperty("user.home"), ".jdks")); + + String javaHome = System.getenv("JAVA_HOME"); + if (javaHome != null && !javaHome.isEmpty()) { + roots.add(new File(javaHome)); + } + + String osName = System.getProperty("os.name", "").toLowerCase(); + if (osName.contains("win")) { + String pf = System.getenv("ProgramW6432"); + if (pf == null) { + pf = System.getenv("ProgramFiles"); + } + + if (pf != null && !pf.isEmpty()) { + roots.add(new File(pf, "Java")); + } + } + + for (File root : roots) { + log("Scanning: " + root.getAbsolutePath()); + + File detected = scan(root); + if (detected != null) { + return detected; + } + } + + return null; } private static File scan(File root) { @@ -320,6 +348,13 @@ private static File scan(File root) { log("Matched Java 25+: " + f.getAbsolutePath()); return f; } + + if (f.isDirectory()) { + File nested = scan(f); + if (nested != null) { + return nested; + } + } } return null; From 85ff21092cd4ec97c5f82ecf5bf68271da92480e Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Wed, 13 May 2026 12:23:53 -0600 Subject: [PATCH 34/40] Put Continue button on top in Bootstrap UI --- .../serivesmejia/eocvsim/Bootstrap.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java b/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java index 6d04681e..2174ddd3 100644 --- a/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java +++ b/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java @@ -73,7 +73,7 @@ public static void main(String[] args) throws Exception { public static final class PromptTest { public static void main(String[] args) { - promptUser(new File("C:/Program Files/Java/jdk-25"), 17); + promptUser(new File("C:/Program Files/Java/jdk-25"), 0); } } @@ -150,7 +150,8 @@ static File promptUser(File detected, int currentJava) { continueBtn.setEnabled(hasDetected); - continueBtn.setPreferredSize(new Dimension(180, 30)); + // Make the continue button long and the other two standard width + continueBtn.setPreferredSize(new Dimension(360, 30)); selectBtn.setPreferredSize(new Dimension(180, 30)); exitBtn.setPreferredSize(new Dimension(180, 30)); @@ -225,20 +226,23 @@ public void windowClosed(WindowEvent e) { gbc.fill = GridBagConstraints.HORIZONTAL; gbc.weightx = 1.0; - // Row 0: Select and Exit + // Row 0: Continue with detected (spans both columns) gbc.gridx = 0; gbc.gridy = 0; + gbc.gridwidth = 2; + gbc.anchor = GridBagConstraints.CENTER; + buttonBlock.add(continueBtn, gbc); + + // Row 1: Select and Exit + gbc.gridwidth = 1; + gbc.gridy = 1; + gbc.gridx = 0; + gbc.anchor = GridBagConstraints.CENTER; buttonBlock.add(selectBtn, gbc); gbc.gridx = 1; buttonBlock.add(exitBtn, gbc); - // Row 1: Continue with detected (spans both columns, centered) - gbc.gridx = 0; - gbc.gridy = 1; - gbc.gridwidth = 2; - gbc.anchor = GridBagConstraints.CENTER; - buttonBlock.add(continueBtn, gbc); root.add(buttonBlock); From c215ac15337401821225d42b7fbfd78da7850419 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Wed, 13 May 2026 16:11:02 -0600 Subject: [PATCH 35/40] Remove natives and shadowJar from maven publications --- Common/build.gradle | 14 -------------- EOCV-Sim/build.gradle | 18 +++++++----------- Vision/build.gradle | 14 -------------- settings.gradle | 3 +-- 4 files changed, 8 insertions(+), 41 deletions(-) diff --git a/Common/build.gradle b/Common/build.gradle index b8d4cb53..435516a0 100644 --- a/Common/build.gradle +++ b/Common/build.gradle @@ -16,23 +16,9 @@ components.java { wpilibTools.deps.wpilibVersion = wpilibVersion -def nativeConfigName = 'wpilibNatives' -def nativeConfig = configurations.create(nativeConfigName) - -def nativeTasks = wpilibTools.createExtractionTasks { - configurationName = nativeConfigName -} - -nativeTasks.addToSourceSetResources(sourceSets.main) - -nativeConfig.dependencies.add wpilibTools.deps.wpilib("wpiutil") -nativeConfig.dependencies.add wpilibTools.deps.wpilib("cscore") -nativeConfig.dependencies.add wpilibTools.deps.wpilibOpenCv(opencvVersion) - dependencies { // WPILib OpenCV replaces openpnp api wpilibTools.deps.wpilibOpenCvJava(opencvVersion) - wpilibNatives wpilibTools.deps.wpilibOpenCv(opencvVersion) implementation "com.moandjiezana.toml:toml4j:$toml4j_version" implementation "info.picocli:picocli:$picocli_version" diff --git a/EOCV-Sim/build.gradle b/EOCV-Sim/build.gradle index 8a0ae9ec..5689b95e 100644 --- a/EOCV-Sim/build.gradle +++ b/EOCV-Sim/build.gradle @@ -36,6 +36,12 @@ tasks.named('compileBootstrapJava', JavaCompile) { options.release.set(8) } +components.java { + tasks.named("shadowJar").configure { + enabled = project.gradle.startParameter.taskNames.contains("shadowJar") + } +} + shadowJar { mergeServiceFiles() archiveClassifier.set(wpilibTools.currentPlatform.platformName) @@ -77,16 +83,6 @@ tasks.register('pack', PackageTask) { } } -tasks.processResources { - from({ - project(":PaperVisionShadow").tasks.shadowJar - }) { - rename { "PaperVisionPlugin.jar" } - into("embedded_plugins") - } -} - - wpilibTools.deps.wpilibVersion = wpilibVersion def nativeConfigName = 'wpilibNatives' @@ -128,7 +124,7 @@ dependencies { implementation "org.slf4j:slf4j-api:$slf4j_version" implementation "org.apache.logging.log4j:log4j-api:$log4j_version" - implementation "org.apache.logging.log4j:log4j-core:2.25.0" + implementation 'org.apache.logging.log4j:log4j-core:2.25.4' implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$log4j_version" implementation "info.picocli:picocli:$picocli_version" diff --git a/Vision/build.gradle b/Vision/build.gradle index c223fdf3..e3cbb8cc 100644 --- a/Vision/build.gradle +++ b/Vision/build.gradle @@ -9,19 +9,6 @@ apply from: '../build.common.gradle' wpilibTools.deps.wpilibVersion = wpilibVersion -def nativeConfigName = 'wpilibNatives' -def nativeConfig = configurations.create(nativeConfigName) - -def nativeTasks = wpilibTools.createExtractionTasks { - configurationName = nativeConfigName -} - -nativeTasks.addToSourceSetResources(sourceSets.main) - -nativeConfig.dependencies.add wpilibTools.deps.wpilib("wpiutil") -nativeConfig.dependencies.add wpilibTools.deps.wpilib("cscore") -nativeConfig.dependencies.add wpilibTools.deps.wpilibOpenCv(opencvVersion) - dependencies { implementation project(':Common') @@ -30,7 +17,6 @@ dependencies { } api wpilibTools.deps.wpilibOpenCvJava(opencvVersion) - wpilibNatives wpilibTools.deps.wpilibOpenCv(opencvVersion) implementation "org.slf4j:slf4j-api:$slf4j_version" implementation 'org.jetbrains.kotlin:kotlin-stdlib' diff --git a/settings.gradle b/settings.gradle index 71144a78..547da593 100644 --- a/settings.gradle +++ b/settings.gradle @@ -20,5 +20,4 @@ rootProject.name = 'EOCV-Sim' include 'TeamCode' include 'EOCV-Sim' include 'Common' -include 'Vision' -include 'PaperVisionShadow' \ No newline at end of file +include 'Vision' \ No newline at end of file From 2f17a31a3dbb30e9821e84df64fef49893dc031f Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Thu, 14 May 2026 03:31:59 -0600 Subject: [PATCH 36/40] Replace AprilTagPlugin with WPILib's apriltag support, keep backwards compatibility --- EOCV-Sim/build.gradle | 21 +- .../serivesmejia/eocvsim/gui/Visualizer.kt | 2 +- .../gui/dialog/source/CreateCameraSource.kt | 11 +- .../serivesmejia/eocvsim/input/InputSource.kt | 6 +- .../eocvsim/input/source/CameraSource.kt | 144 ++++------ .../eocvsim/input/source/HttpSource.kt | 6 +- .../eocvsim/input/source/ImageSource.kt | 18 +- .../eocvsim/input/source/NullSource.kt | 5 +- .../eocvsim/input/source/VideoSource.kt | 24 +- .../plugin/api/impl/InputSourceApisImpl.kt | 14 +- .../eocvsim/util/LibraryLoader.java | 12 +- .../util/template/GradleWorkspaceTemplate.kt | 11 +- .../eocvsim/input/VisionInputSource.kt | 41 +-- .../input/VisionInputSourceProvider.kt | 40 +-- .../plugin/loader/PluginClassLoader.kt | 2 +- Vision/build.gradle | 9 +- .../external/FrameReceiverOpenCvCamera.java | 12 +- .../vision/external/PipelineRenderHook.kt | 10 +- .../external/gui/SwingOpenCvViewport.kt | 3 +- .../vision/external/source/VisionSource.java | 14 +- .../external/source/VisionSourceBase.java | 2 +- .../openftc/apriltag/AprilTagDetection.java | 34 +++ .../apriltag/AprilTagDetectionCache.java | 45 +++ .../openftc/apriltag/AprilTagDetectorJNI.java | 125 +++++++++ .../org/openftc/apriltag/AprilTagPose.java | 32 +++ .../apriltag/ApriltagDetectionJNI.java | 258 ++++++++++++++++++ build.gradle | 3 +- 27 files changed, 684 insertions(+), 220 deletions(-) create mode 100644 Vision/src/main/java/org/openftc/apriltag/AprilTagDetection.java create mode 100644 Vision/src/main/java/org/openftc/apriltag/AprilTagDetectionCache.java create mode 100644 Vision/src/main/java/org/openftc/apriltag/AprilTagDetectorJNI.java create mode 100644 Vision/src/main/java/org/openftc/apriltag/AprilTagPose.java create mode 100644 Vision/src/main/java/org/openftc/apriltag/ApriltagDetectionJNI.java diff --git a/EOCV-Sim/build.gradle b/EOCV-Sim/build.gradle index 5689b95e..27f696e5 100644 --- a/EOCV-Sim/build.gradle +++ b/EOCV-Sim/build.gradle @@ -113,23 +113,37 @@ dependencies { api wpilibTools.deps.wpilibOpenCvJava(opencvVersion) wpilibNatives wpilibTools.deps.wpilibOpenCv(opencvVersion) - // use cscore for webcams + // WPILib AprilTag replaces AprilTagDesktop + api wpilibTools.deps.wpilibJava("apriltag") + wpilibNatives wpilibTools.deps.wpilib("apriltag") + + // WPILib cscore replaces steve implementation wpilibTools.deps.wpilibJava("cscore") wpilibNatives wpilibTools.deps.wpilib("cscore") + // cscore depends on these implementation wpilibTools.deps.wpilibJava("wpinet") wpilibNatives wpilibTools.deps.wpilib("wpinet") + implementation wpilibTools.deps.wpilibJava("wpiutil") wpilibNatives wpilibTools.deps.wpilib("wpiutil") + // needed for apriltags + implementation wpilibTools.deps.wpilibJava("wpimath") + wpilibNatives wpilibTools.deps.wpilib("wpimath") + + // we don't use this at all... But if we don't have it in the classpath, + // we can't use WPILib's Geometry classes for apriltags. + implementation "us.hebi.quickbuf:quickbuf-runtime:$quickbufVersion" + implementation "org.slf4j:slf4j-api:$slf4j_version" implementation "org.apache.logging.log4j:log4j-api:$log4j_version" implementation 'org.apache.logging.log4j:log4j-core:2.25.4' implementation "org.apache.logging.log4j:log4j-slf4j2-impl:$log4j_version" implementation "info.picocli:picocli:$picocli_version" - implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' - implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.15.2' + implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version" + implementation "com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version" implementation "io.github.classgraph:classgraph:$classgraph_version" implementation "com.formdev:flatlaf:$flatlaf_version" @@ -186,7 +200,6 @@ public final class Build { public static final String packagePlatform = "${wpilibTools.currentPlatform.platformName}"; public static final String opencvVersion = "$opencvVersion"; - public static final String apriltagPluginVersion = "$apriltag_plugin_version"; public static final String paperVisionVersion = "$papervision_version"; } """ diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt index d5e130fd..e881f4d2 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt @@ -461,4 +461,4 @@ class Visualizer : PhaseOrchestrableBase(), KoinComponent { } } } - + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt index 4d81217a..452301f2 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog.source import com.github.serivesmejia.eocvsim.gui.Visualizer @@ -196,7 +196,6 @@ class CreateCameraSource : KoinComponent { val info = cameraInfos.getOrNull(index) ?: return try { - val cam = UsbCamera(info.name, info.dev) modes = cam.enumerateVideoModes() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt index b4c96713..42e5beb0 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSource.kt @@ -53,9 +53,7 @@ abstract class InputSource : Comparable, KoinComponent { abstract fun onPause() abstract fun onResume() - open fun setSize(size: Size) {} - - open fun getSize() = Size() + abstract val sourceSize: Size open fun update(): Mat? = null @@ -77,4 +75,4 @@ abstract class InputSource : Comparable, KoinComponent { } } - + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt index 0a1052f8..e46abf4b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt @@ -35,13 +35,13 @@ class CameraSource : InputSource, KoinComponent { @JvmStatic var currentWebcamIndex = -1 } - override val hasSlowInitialization: Boolean get() = true + override val hasSlowInitialization = true - @JsonProperty @JvmField var cameraPortIndex: Int = -1 - @JsonProperty @JvmField var exactPortMatch: Boolean = false + @JsonProperty @JvmField var cameraPortIndex = -1 + @JsonProperty @JvmField var exactPortMatch = false @JsonProperty @JvmField var vendorId: Int? = null @JsonProperty @JvmField var productId: Int? = null - @Transient var webcamName: String = "" + @Transient var webcamName = "" @JsonProperty @JvmField var videoMode: VideoMode? = null @@ -49,14 +49,11 @@ class CameraSource : InputSource, KoinComponent { private set @Transient private var cvSink: CvSink? = null - @Transient private var lastFrame = Mat() - @Transient private var initialized = false - @Transient var isLegacyByIndex = false + @Transient private var capTimeNanos = 0L - @Transient private var capTimeNanos: Long = 0 private val configManager: ConfigManager by inject() private val logger by loggerForThis() @@ -96,94 +93,65 @@ class CameraSource : InputSource, KoinComponent { this.isLegacyByIndex = true } - override fun setSize(size: Size) { - // deprecated concept now; derived from VideoMode - } - - override fun getSize(): Size = - videoMode?.let { Size(it.width.toDouble(), it.height.toDouble()) } ?: Size() + override val sourceSize: Size + get() = videoMode?.let { Size(it.width.toDouble(), it.height.toDouble()) } ?: Size() override fun init(): Boolean { if (initialized) return false initialized = true + val cameras by lazy { UsbCamera.enumerateUsbCameras() } + val matchedInfo = when { - exactPortMatch -> { - val infos = UsbCamera.enumerateUsbCameras() - infos.firstOrNull { - cameraPortIndex >= 0 && + exactPortMatch -> cameras.firstOrNull { + cameraPortIndex >= 0 && it.dev == cameraPortIndex && vendorId != null && productId != null && it.vendorId == vendorId && it.productId == productId - } ?: run { - logger.error("Camera not found on the same connection: $cameraPortIndex") + } ?: run { + logger.error("Camera not found on the same connection: $cameraPortIndex") + return false + } + + cameraPortIndex >= 0 -> cameras.firstOrNull { it.dev == cameraPortIndex } + ?: run { + logger.error("Camera not found on port: $cameraPortIndex") return false } - } - cameraPortIndex >= 0 -> { - val infos = UsbCamera.enumerateUsbCameras() - infos.firstOrNull { it.dev == cameraPortIndex } - ?: run { - logger.error("Camera not found on port: $cameraPortIndex") - return false - } + vendorId != null && productId != null -> cameras.firstOrNull { + it.vendorId == vendorId && it.productId == productId + } ?: run { + logger.error("Camera not found by VID/PID: $vendorId:$productId") + return false } - vendorId != null && productId != null -> { - val infos = UsbCamera.enumerateUsbCameras() - infos.firstOrNull { - it.vendorId == vendorId && it.productId == productId - } ?: run { - logger.error("Camera not found by VID/PID: ${vendorId}:${productId}") + webcamName.isNotEmpty() -> cameras.firstOrNull { it.name == webcamName } + ?: run { + logger.error("Camera not found: $webcamName") return false } - } - - webcamName.isNotEmpty() -> { - val infos = UsbCamera.enumerateUsbCameras() - infos.firstOrNull { it.name == webcamName } - ?: run { - logger.error("Camera not found: $webcamName") - return false - } - } else -> null } - val cam = if (matchedInfo != null) { + camera = if (matchedInfo != null) { webcamName = matchedInfo.name UsbCamera(matchedInfo.name, matchedInfo.dev) } else { UsbCamera("$cameraPortIndex", cameraPortIndex) } - camera = cam - - val desiredMode = videoMode - - if (desiredMode != null) { - cam.videoMode = desiredMode - } else { - val mode = cam.videoMode - cam.videoMode = mode - } - - val mode = cam.videoMode + camera!!.videoMode = videoMode ?: camera!!.videoMode - logger.info( - "Camera started: ${matchedInfo?.name ?: webcamName.ifEmpty { "Camera $cameraPortIndex" }} ${mode?.stringify()}" - ) + logger.info("Camera started: ${matchedInfo?.name ?: webcamName.ifEmpty { "Camera $cameraPortIndex" }} ${camera!!.videoMode?.stringify()}") cvSink = CvSink("eocvsim_sink_$cameraPortIndex", PixelFormat.BGR).also { - it.source = cam + it.source = camera } - val ok = cvSink!!.grabFrame(lastFrame, configManager.config.webcamOpenTimeoutSec) - - if (ok == 0L || lastFrame.empty()) { + if (cvSink!!.grabFrame(lastFrame, configManager.config.webcamOpenTimeoutSec) == 0L || lastFrame.empty()) { logger.error("Failed to open camera: ${cvSink!!.error}") return false } @@ -194,61 +162,51 @@ class CameraSource : InputSource, KoinComponent { override fun reset() { if (!initialized) return - - cvSink?.close() - cvSink = null - - camera?.close() - camera = null - + teardown() lastFrame.release() - initialized = false } - override fun close() { - cvSink?.close() - camera?.close() - currentWebcamIndex = -1 - } + override fun close() = teardown() override fun update(): Mat { if (isPaused) return lastFrame - val grabTime = cvSink?.grabFrame(lastFrame, configManager.config.webcamNewFrameTimeoutSec) ?: 0L + capTimeNanos = cvSink?.grabFrame(lastFrame, configManager.config.webcamNewFrameTimeoutSec) ?: 0L - if(lastFrame.empty()) { - return lastFrame + if (!lastFrame.empty()) { + Imgproc.cvtColor(lastFrame, lastFrame, Imgproc.COLOR_BGR2RGBA) } - capTimeNanos = grabTime - Imgproc.cvtColor(lastFrame, lastFrame, Imgproc.COLOR_BGR2RGBA) return lastFrame } override fun onPause() { cvSink?.grabFrame(lastFrame, configManager.config.webcamNewFrameTimeoutSec) - cvSink?.close() - camera?.close() - currentWebcamIndex = -1 + teardown() } override fun onResume() { InputSourceInitializer.runWithTimeout(this) { init() } } + private fun teardown() { + cvSink?.close() + cvSink = null + camera?.close() + camera = null + currentWebcamIndex = -1 + } + override fun internalCloneSource(): InputSource = - if (isLegacyByIndex) { - CameraSource(cameraPortIndex, videoMode) - } else { - CameraSource(webcamName, cameraPortIndex, exactPortMatch, vendorId, productId, videoMode) - } + if (isLegacyByIndex) CameraSource(cameraPortIndex, videoMode) + else CameraSource(webcamName, cameraPortIndex, exactPortMatch, vendorId, productId, videoMode) - override val fileFilters: FileFilter? get() = null - override val captureTimeNanos: Long get() = capTimeNanos + override val fileFilters: FileFilter? = null + override val captureTimeNanos get() = capTimeNanos override fun toString() = "CameraSource($webcamName, port=$cameraPortIndex, exactPortMatch=$exactPortMatch, vid=$vendorId, pid=$productId, ${videoMode?.stringify()})" private fun VideoMode.stringify() = "${width}x${height}@${fps} $pixelFormat" -} +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt index 3fbed8c9..eb1fff1e 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt @@ -14,6 +14,7 @@ import io.github.deltacv.common.util.loggerForThis import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.opencv.core.Mat +import org.opencv.core.Size import org.opencv.imgproc.Imgproc import org.wpilib.vision.camera.CvSink import org.wpilib.vision.camera.HttpCamera @@ -110,6 +111,9 @@ class HttpSource @JvmOverloads constructor ( override fun internalCloneSource(): InputSource = HttpSource(url) + override val sourceSize: Size + get() = cvSink?.directMat?.size() ?: Size() + override val fileFilters: FileFilter? get() = null override val captureTimeNanos: Long get() = capTimeNanos @@ -117,4 +121,4 @@ class HttpSource @JvmOverloads constructor ( return "HttpSource($url)" } } - + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.kt index 328d0c5f..9bac42df 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/ImageSource.kt @@ -36,6 +36,8 @@ class ImageSource @JvmOverloads constructor( @Transient private var matRecycler = MatRecycler(2) + override val sourceSize get() = size + override fun init(): Boolean { if (initialized) return false initialized = true @@ -89,13 +91,12 @@ class ImageSource @JvmOverloads constructor( readMat.copyTo(img) readMat.release() - if (this.size.area() != 0.0) { - Imgproc.resize(img, img, this.size, 0.0, 0.0, Imgproc.INTER_AREA) + if (this.sourceSize.area() != 0.0) { + Imgproc.resize(img, img, this.sourceSize, 0.0, 0.0, Imgproc.INTER_AREA) } else { this.size = img!!.size() } - Imgproc.cvtColor(img, img, Imgproc.COLOR_BGR2RGB) } @@ -114,21 +115,14 @@ class ImageSource @JvmOverloads constructor( readImage() } - override fun internalCloneSource() = ImageSource(imgPath, size) - - override fun setSize(size: Size) { - this.size = size - } - - override fun getSize() = size - + override fun internalCloneSource() = ImageSource(imgPath, sourceSize) override val fileFilters: FileFilter get() = FileFilters.imagesFilter override val captureTimeNanos: Long get() = System.nanoTime() override fun toString(): String { - return "ImageSource(\"$imgPath\", $size)" + return "ImageSource(\"$imgPath\", $sourceSize)" } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/NullSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/NullSource.kt index 1e92349b..e94c3e61 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/NullSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/NullSource.kt @@ -7,6 +7,7 @@ package com.github.serivesmejia.eocvsim.input.source import com.github.serivesmejia.eocvsim.input.InputSource import org.opencv.core.Mat +import org.opencv.core.Size import javax.swing.filechooser.FileFilter class NullSource : InputSource() { @@ -25,6 +26,8 @@ class NullSource : InputSource() { override fun onResume() {} + override val sourceSize = Size() + override fun internalCloneSource(): InputSource = NullSource() override val fileFilters: FileFilter? = null @@ -32,4 +35,4 @@ class NullSource : InputSource() { } - + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt index 5b3a4fc7..25119513 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/VideoSource.kt @@ -50,6 +50,8 @@ class VideoSource @JvmOverloads constructor( @Transient private val logger = LoggerFactory.getLogger(javaClass) + override val sourceSize get() = size + override fun init(): Boolean { if (initialized) return false initialized = true @@ -136,10 +138,10 @@ class VideoSource @JvmOverloads constructor( return lastFrame } - if (size.area() == 0.0) size = lastFrame!!.size() + if (sourceSize.area() == 0.0) size = lastFrame!!.size() Imgproc.cvtColor(newFrame, lastFrame, Imgproc.COLOR_BGR2RGB) - Imgproc.resize(lastFrame, lastFrame, size, 0.0, 0.0, Imgproc.INTER_AREA) + Imgproc.resize(lastFrame, lastFrame, sourceSize, 0.0, 0.0, Imgproc.INTER_AREA) matRecycler?.returnMat(newFrame) @@ -155,7 +157,7 @@ class VideoSource @JvmOverloads constructor( lastFramePaused?.let { Imgproc.cvtColor(it, it, Imgproc.COLOR_BGR2RGB) - Imgproc.resize(it, it, size, 0.0, 0.0, Imgproc.INTER_AREA) + Imgproc.resize(it, it, sourceSize, 0.0, 0.0, Imgproc.INTER_AREA) } update() @@ -172,23 +174,11 @@ class VideoSource @JvmOverloads constructor( video!!.set(Videoio.CAP_PROP_POS_FRAMES, lastFramePosition) } - override fun internalCloneSource(): InputSource = VideoSource(videoPath, size) - - override fun getSize(): Size = size - - - override fun setSize(size: Size) { - this.size = size - } + override fun internalCloneSource(): InputSource = VideoSource(videoPath, sourceSize) override val fileFilters: FileFilter get() = FileFilters.videoMediaFilter override val captureTimeNanos: Long get() = capTimeNanos - - override fun toString(): String { - return "VideoSource($videoPath, $size)" - - } - + override fun toString() = "VideoSource($videoPath, $sourceSize)" } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt index 9bf4b52b..a103df89 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.input.InputSourceManager @@ -26,8 +26,8 @@ class InputSourceApiImpl(owner: EOCVSimPlugin, val internalInputSource: com.gith internalInputSource.videoMode?.height?.toDouble() ?: 0.0 ) ) - is ImageSource -> New.Image(internalInputSource.imgPath, internalInputSource.size) - is VideoSource -> New.Video(internalInputSource.videoPath, internalInputSource.size) + is ImageSource -> New.Image(internalInputSource.imgPath, internalInputSource.sourceSize) + is VideoSource -> New.Video(internalInputSource.videoPath, internalInputSource.sourceSize) is HttpSource -> New.Http(internalInputSource.url) else -> throw IllegalStateException("Unknown input source type: ${internalInputSource::class.java}") } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/LibraryLoader.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/LibraryLoader.java index 4943504b..0f349199 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/LibraryLoader.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/LibraryLoader.java @@ -8,8 +8,10 @@ import java.io.IOException; import org.opencv.core.Core; +import org.wpilib.math.jni.WPIMathJNI; import org.wpilib.net.WPINetJNI; import org.wpilib.util.WPIUtilJNI; +import org.wpilib.vision.apriltag.jni.AprilTagJNI; import org.wpilib.vision.camera.CameraServerJNI; import org.wpilib.vision.camera.OpenCvLoader; @@ -18,18 +20,18 @@ public record Result(boolean success, Throwable error) {} public static Result loadLibraries() { WPIUtilJNI.Helper.setExtractOnStaticLoad(false); + AprilTagJNI.Helper.setExtractOnStaticLoad(false); + WPIMathJNI.Helper.setExtractOnStaticLoad(false); + WPINetJNI.Helper.setExtractOnStaticLoad(false); CameraServerJNI.Helper.setExtractOnStaticLoad(false); OpenCvLoader.Helper.setExtractOnStaticLoad(false); - WPINetJNI.Helper.setExtractOnStaticLoad(false); try { CombinedRuntimeLoader.loadLibraries(LibraryLoader.class, "wpiutiljni"); WPIUtilJNI.checkMsvcRuntime(); - CombinedRuntimeLoader.loadLibraries( - LibraryLoader.class, - "wpinetjni", - "cscorejni"); + CombinedRuntimeLoader.loadLibraries(LibraryLoader.class, + "apriltagjni", "wpimathjni", "wpinetjni", "cscorejni"); CombinedRuntimeLoader.loadLibraries(LibraryLoader.class, Core.NATIVE_LIBRARY_NAME); } catch(IOException | UnsatisfiedLinkError e) { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/GradleWorkspaceTemplate.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/GradleWorkspaceTemplate.kt index 4b074437..c81a5af8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/GradleWorkspaceTemplate.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/GradleWorkspaceTemplate.kt @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.workspace.util.template import com.github.serivesmejia.eocvsim.Build @@ -54,7 +54,6 @@ object GradleWorkspaceTemplate : WorkspaceTemplate() { val fileContents = SysUtil.loadFileStr(buildGradleFile) .replace("\$eocvsim_version", Build.standardVersionString) .replace("\$opencv_version", Build.opencvVersion) - .replace("\$apriltag_version", Build.apriltagPluginVersion) SysUtil.saveFileStr(buildGradleFile, fileContents) } diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSource.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSource.kt index af142d3e..e696b4b2 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSource.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSource.kt @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.input import com.github.serivesmejia.eocvsim.input.InputSource @@ -16,34 +16,37 @@ import org.opencv.core.Mat import org.opencv.core.Size class VisionInputSource( - private val inputSource: InputSource, - throwableHandler: ThrowableHandler? = null + throwableHandler: ThrowableHandler? = null, + private val inputSourceProvider: (Size) -> InputSource, ) : VisionSourceBase(throwableHandler) { val logger by loggerForThis() - override fun init(): Int { - return 0 + private var inputSource: InputSource? = null + + override fun check(): Int { + val testSource = inputSourceProvider(Size(0.0, 0.0)) + val state = testSource.init() + testSource.close() + return if(state) 0 else 1 } override fun getControlMap() = if(inputSource is CameraSource) { - CameraSourceControlMap(inputSource) - } else throw IllegalStateException("Controls are not available for source ${inputSource.name}") + CameraSourceControlMap(inputSource!! as CameraSource) + } else throw IllegalStateException("Controls are not available for source ${inputSource?.name ?: ""}") override fun close(): Boolean { - inputSource.close() - inputSource.reset() + inputSource?.close() return true } override fun startSource(size: Size): Boolean { - inputSource.setSize(size) - inputSource.init() - return true + inputSource = inputSourceProvider(size) + return inputSource!!.init() } override fun stopSource(): Boolean { - inputSource.close() + inputSource?.close() return true; } @@ -51,8 +54,8 @@ class VisionInputSource( override fun pullFrame(): Timestamped { return try { - val frame = inputSource.update(); - Timestamped(frame, inputSource.captureTimeNanos) + val frame = inputSource!!.update(); + Timestamped(frame, inputSource!!.captureTimeNanos) } catch(e: Exception) { Timestamped(emptyMat, 0) } diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt index 821d6174..6402da70 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.eocvsim.input import com.github.serivesmejia.eocvsim.input.InputSourceManager @@ -45,28 +45,36 @@ class VisionInputSourceProvider( return ok } - private fun defaultMode(index: Int): VideoMode { + private fun defaultMode(index: Int, size: Size): VideoMode { val cam = UsbCamera("$index", index) val mode = try { - cam.videoMode + val modes = cam.enumerateVideoModes() + if (size.width > 0 && size.height > 0) { + modes.firstOrNull { it.width == size.width.toInt() && it.height == size.height.toInt() } + ?: modes.firstOrNull() + ?: cam.videoMode + } else { + cam.videoMode + } } catch (_: Exception) { - // fallback safe mode if enumeration fails VideoMode(0, 640, 480, 30) + } finally { + cam.close() } - cam.close() return mode } override fun get(name: String): VisionSource { - val source = VisionInputSource( + RedirectToOpModeThrowableHandler(notifier) + ) { size -> when { File(name).exists() -> { when { - isImage(name) -> ImageSource(name, Size()) - isVideo(name) -> VideoSource(name, Size()) + isImage(name) -> ImageSource(name, size) + isVideo(name) -> VideoSource(name, size) else -> throw IllegalArgumentException( "File $name is neither image nor video" ) @@ -81,13 +89,11 @@ class VisionInputSourceProvider( inputSourceManager.sources[name] ?: throw IllegalArgumentException("Input source $name not found") } else { - // 🔥 FIX: use real VideoMode instead of Size hack - CameraSource(index, defaultMode(index)) + CameraSource(index, defaultMode(index, size)) } } - }, - RedirectToOpModeThrowableHandler(notifier) - ) + } + } notifier.onStateChange { when (notifier.state) { diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginClassLoader.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginClassLoader.kt index 7339e020..33b6e9d3 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginClassLoader.kt +++ b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginClassLoader.kt @@ -164,7 +164,7 @@ class PluginClassLoader( if (entry != null) { // Construct a URL for the resource inside the plugin JAR - return URL("jar:file:${pluginJar.absolutePath}!/$name") + return URI.create("jar:file:${pluginJar.absolutePath}!/$name").toURL() } else { resourceFromClasspath(name)?.let { return it } } diff --git a/Vision/build.gradle b/Vision/build.gradle index e3cbb8cc..f517f1f9 100644 --- a/Vision/build.gradle +++ b/Vision/build.gradle @@ -12,11 +12,10 @@ wpilibTools.deps.wpilibVersion = wpilibVersion dependencies { implementation project(':Common') - api("org.deltacv:AprilTagDesktop:$apriltag_plugin_version") { - transitive = false - } - - api wpilibTools.deps.wpilibOpenCvJava(opencvVersion) + compileOnly wpilibTools.deps.wpilibOpenCvJava(opencvVersion) + compileOnly wpilibTools.deps.wpilibJava("wpiutil") + compileOnly wpilibTools.deps.wpilibJava("wpimath") + compileOnly wpilibTools.deps.wpilibJava("apriltag") implementation "org.slf4j:slf4j-api:$slf4j_version" implementation 'org.jetbrains.kotlin:kotlin-stdlib' diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/FrameReceiverOpenCvCamera.java b/Vision/src/main/java/io/github/deltacv/vision/external/FrameReceiverOpenCvCamera.java index 963e9db3..1110ed82 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/FrameReceiverOpenCvCamera.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/FrameReceiverOpenCvCamera.java @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external; import io.github.deltacv.vision.external.source.VisionSource; @@ -37,7 +37,7 @@ public FrameReceiverOpenCvCamera(VisionSource source, OpenCvViewport handedViewp public int openCameraDevice() { prepareForOpenCameraDevice(); - return source.init(); + return source.check(); } @Override diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/PipelineRenderHook.kt b/Vision/src/main/java/io/github/deltacv/vision/external/PipelineRenderHook.kt index 48e47f1d..8c9eecd6 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/PipelineRenderHook.kt +++ b/Vision/src/main/java/io/github/deltacv/vision/external/PipelineRenderHook.kt @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external import android.graphics.Canvas diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt b/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt index 21660e8f..64f098aa 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt +++ b/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt @@ -385,6 +385,7 @@ class SwingOpenCvViewport( override fun setRenderingPolicy(policy: ViewportRenderingPolicy) {} override fun setRenderHook(renderHook: RenderHook) { + logger.debug("setRenderHook(): ${renderHook::class.simpleName}") this.renderHook = renderHook } @@ -399,4 +400,4 @@ class SwingOpenCvViewport( private const val FRAMEBUFFER_RECYCLER_CAPACITY = VISION_PREVIEW_FRAME_QUEUE_CAPACITY + 4 //So that the evicting queue can be full, and the render thread has one checked out (+1) and post() can still take one (+1). } -} +} diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSource.java b/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSource.java index 9071775d..d09d473b 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSource.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSource.java @@ -1,19 +1,19 @@ -/* - * Copyright (c) 2023 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2023 Sebastian Erives + * Licensed under the MIT License. + */ + package io.github.deltacv.vision.external.source; import org.opencv.core.Size; public interface VisionSource { - int init(); + int check(); CameraControlMap getControlMap(); - boolean start(Size requestedSize); + boolean start(Size size); boolean attach(FrameReceiver sourced); boolean remove(FrameReceiver sourced); diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceBase.java b/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceBase.java index 3b47bb56..a13093b2 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceBase.java +++ b/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceBase.java @@ -144,4 +144,4 @@ public void run() { } } - + diff --git a/Vision/src/main/java/org/openftc/apriltag/AprilTagDetection.java b/Vision/src/main/java/org/openftc/apriltag/AprilTagDetection.java new file mode 100644 index 00000000..e19e7ffc --- /dev/null +++ b/Vision/src/main/java/org/openftc/apriltag/AprilTagDetection.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.openftc.apriltag; + +import org.opencv.core.Point; + +public class AprilTagDetection +{ + public int id; + public int hamming; + public float decisionMargin; + public Point center; + public Point[] corners; + public AprilTagPose pose; +} \ No newline at end of file diff --git a/Vision/src/main/java/org/openftc/apriltag/AprilTagDetectionCache.java b/Vision/src/main/java/org/openftc/apriltag/AprilTagDetectionCache.java new file mode 100644 index 00000000..d1dc1cd2 --- /dev/null +++ b/Vision/src/main/java/org/openftc/apriltag/AprilTagDetectionCache.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.openftc.apriltag; + +import org.wpilib.vision.apriltag.AprilTagDetection; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +/** + * Internal cache for AprilTag detection arrays to simulate pointer-based returns + */ +class AprilTagDetectionCache +{ + private static final Map cache = new HashMap<>(); + private static final AtomicLong pointerCounter = new AtomicLong(1); + + /** + * Cache a detection array and return a fake pointer ID + */ + static long cacheDetections(AprilTagDetection[] detections) { + long fakePtr = pointerCounter.getAndIncrement(); + cache.put(fakePtr, detections); + return fakePtr; + } + + /** + * Retrieve cached detections by fake pointer ID + */ + static AprilTagDetection[] getDetections(long fakePtr) { + return cache.get(fakePtr); + } + + /** + * Remove cached detections by fake pointer ID + */ + static void freeDetections(long fakePtr) { + cache.remove(fakePtr); + } +} + diff --git a/Vision/src/main/java/org/openftc/apriltag/AprilTagDetectorJNI.java b/Vision/src/main/java/org/openftc/apriltag/AprilTagDetectorJNI.java new file mode 100644 index 00000000..ae3f5664 --- /dev/null +++ b/Vision/src/main/java/org/openftc/apriltag/AprilTagDetectorJNI.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2021 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.openftc.apriltag; + +import org.opencv.core.Mat; +import org.wpilib.vision.apriltag.AprilTagDetector; +import org.wpilib.vision.apriltag.jni.AprilTagJNI; + +import java.util.ArrayList; + +public class AprilTagDetectorJNI +{ + public enum TagFamily + { + TAG_36h11("tag36h11"), + TAG_25h9("tag25h9"), + TAG_16h5("tag16h5"), + TAG_standard41h12("tagStandard41h12"); + + public final String string; + + TagFamily(String string) + { + this.string = string; + } + } + + /** + * Create a new AprilTag detector + * @param tagFamily the tag family the detector will use + * @param decimate the decimation factor + * @param threads the number of threads to use + * @return a native pointer to the newly created detector + */ + public static long createApriltagDetector(String tagFamily, float decimate, int threads) { + long handle = AprilTagJNI.createDetector(); + AprilTagJNI.addFamily(handle, tagFamily, 2); + + AprilTagDetector.Config config = new AprilTagDetector.Config(); + config.quadDecimate = decimate; + config.numThreads = threads; + AprilTagJNI.setDetectorConfig(handle, config); + + return handle; + } + + /** + * Set the decimation parameter of a previously created AprilTag detector + * @param ptrDetector native pointer to an AprilTag detector, obtained from {@link #createApriltagDetector(String, float, int)} + * @param decimate the new decimation value + */ + public static void setApriltagDetectorDecimation(long ptrDetector, float decimate) { + AprilTagDetector.Config config = AprilTagJNI.getDetectorConfig(ptrDetector); + config.quadDecimate = decimate; + AprilTagJNI.setDetectorConfig(ptrDetector, config); + } + + /** + * Run an AprilTag detector on a greyscale image + * @param ptrDetector native pointer to an AprilTag detector, obtained from {@link #createApriltagDetector(String, float, int)} + * @param ptrGreyscaleBuf native pointer to a greyscale image buffer + * @param width the width of the greyscale buffer + * @param height the height of the greyscale buffer + * @return a native pointer to a list of detections, or 0 if nothing was detected. + * If non-zero, must be freed with {@link ApriltagDetectionJNI#freeDetectionList(long)} + */ + public static long runApriltagDetector(long ptrDetector, long ptrGreyscaleBuf, int width, int height) { + org.wpilib.vision.apriltag.AprilTagDetection[] detections = AprilTagJNI.detect(ptrDetector, width, height, width, ptrGreyscaleBuf); + if (detections == null || detections.length == 0) { + return 0; + } + return AprilTagDetectionCache.cacheDetections(detections); + } + + /** + * Run an AprilTag detector on a greyscale image + * @param ptrDetector native pointer to an AprilTag detector, obtained from {@link #createApriltagDetector(String, float, int)} + * @param grey a greyscale OpenCV Mat + * @param tagSize size of the tag in meters + * @param fx lens intrinsics fx + * @param fy lens intrinsics fy + * @param cx lens intrinsics cx + * @param cy lens intrinsics cy + * @return an ArrayList of AprilTag detections. No care need be taken to release native detections. + */ + public static ArrayList runAprilTagDetectorSimple(long ptrDetector, Mat grey, double tagSize, double fx, double fy, double cx, double cy) + { + ArrayList detections = new ArrayList<>(); + long ptrDetectionArray = runApriltagDetector(ptrDetector, grey.dataAddr(), grey.width(), grey.height()); + if(ptrDetectionArray != 0) + { + detections = ApriltagDetectionJNI.getDetections(ptrDetectionArray, tagSize, fx, fy, cx, cy); + ApriltagDetectionJNI.freeDetectionList(ptrDetectionArray); + } + + return detections; + } + + /** + * Release an AprilTag detector + * @param ptr native pointer to an AprilTag detector, obtained from {@link #createApriltagDetector(String, float, int)} + */ + public static void releaseApriltagDetector(long ptr) { + AprilTagJNI.destroyDetector(ptr); + } +} \ No newline at end of file diff --git a/Vision/src/main/java/org/openftc/apriltag/AprilTagPose.java b/Vision/src/main/java/org/openftc/apriltag/AprilTagPose.java new file mode 100644 index 00000000..de6be1cd --- /dev/null +++ b/Vision/src/main/java/org/openftc/apriltag/AprilTagPose.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2021 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.openftc.apriltag; + +import org.firstinspires.ftc.robotcore.external.matrices.MatrixF; + +public class AprilTagPose +{ + public double x; + public double y; + public double z; + public MatrixF R; +} \ No newline at end of file diff --git a/Vision/src/main/java/org/openftc/apriltag/ApriltagDetectionJNI.java b/Vision/src/main/java/org/openftc/apriltag/ApriltagDetectionJNI.java new file mode 100644 index 00000000..56f6914c --- /dev/null +++ b/Vision/src/main/java/org/openftc/apriltag/ApriltagDetectionJNI.java @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2021 OpenFTC Team + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package org.openftc.apriltag; + +import org.wpilib.math.geometry.Rotation3d; +import org.wpilib.math.geometry.Transform3d; +import org.firstinspires.ftc.robotcore.external.matrices.GeneralMatrixF; +import org.opencv.core.Point; +import org.wpilib.vision.apriltag.AprilTagPoseEstimate; +import org.wpilib.vision.apriltag.AprilTagPoseEstimator; + +import java.util.ArrayList; + +public class ApriltagDetectionJNI +{ + /** + * Get the tag ID of a detection + * @param ptr pointer to a detection, obtained from {@link #getDetectionPointers(long)} + * @return the tag ID + */ + public static int getId(long ptr) + { + return getSingleDetection(ptr).getId(); + } + + /** + * Get the hamming of a detection + * @param ptr pointer to a detection, obtained from {@link #getDetectionPointers(long)} + * @return the hamming value + */ + public static int getHamming(long ptr) + { + return getSingleDetection(ptr).getHamming(); + } + + /** + * Get the decision margin of a detection + * @param ptr pointer to a detection, obtained from {@link #getDetectionPointers(long)} + * @return the decision margin of the detection + */ + public static float getDecisionMargin(long ptr) + { + return getSingleDetection(ptr).getDecisionMargin(); + } + + /** + * Get the centerpoint of a detection + * @param ptr pointer to a detection, obtained from {@link #getDetectionPointers(long)} + * @return the centerpoint of the detection + */ + public static double[] getCenterpoint(long ptr) + { + org.wpilib.vision.apriltag.AprilTagDetection det = getSingleDetection(ptr); + return new double[]{ det.getCenterX(), det.getCenterY() }; + } + + /** + * Get the corners of a detection + * @param ptr pointer to a detection, obtained from {@link #getDetectionPointers(long)} + * @return the corners of the detection + */ + public static double[][] getCorners(long ptr) + { + org.wpilib.vision.apriltag.AprilTagDetection det = getSingleDetection(ptr); + + // WPILib gives corners as a flat double[] of length 8: [x0,y0, x1,y1, x2,y2, x3,y3] + double[] flat = det.getCorners(); + + double[][] corners = new double[4][2]; + for (int i = 0; i < 4; i++) + { + corners[i][0] = flat[i * 2]; + corners[i][1] = flat[i * 2 + 1]; + } + return corners; + } + + /** + * Get the pose estimate for a detection + * @param ptr pointer to a detection, obtained from {@link #getDetectionPointers(long)} + * @param tagSize size of the tag in meters + * @param fx lens intrinsics fx + * @param fy lens intrinsics fy + * @param cx lens intrinsics cx + * @param cy lens intrinsics cy + * @return pose estimate for the detection. 0-2 are translation XYZ, 3-5 are yaw, pitch, roll + */ + public static double[] getPoseEstimate(long ptr, double tagSize, double fx, double fy, double cx, double cy) + { + org.wpilib.vision.apriltag.AprilTagDetection det = getSingleDetection(ptr); + + AprilTagPoseEstimator estimator = new AprilTagPoseEstimator( + new AprilTagPoseEstimator.Config(tagSize, fx, fy, cx, cy)); + + AprilTagPoseEstimate result = estimator.estimateOrthogonalIteration(det, 50); + + Transform3d pose = (result.error1 <= result.error2) ? result.pose1 : result.pose2; + + double tx = pose.getTranslation().getX(); + double ty = pose.getTranslation().getY(); + double tz = pose.getTranslation().getZ(); + + Rotation3d rot = pose.getRotation(); + double[] rotMatrix = quaternionToRotationMatrix( + rot.getQuaternion().getW(), + rot.getQuaternion().getX(), + rot.getQuaternion().getY(), + rot.getQuaternion().getZ() + ); + + double[] out = new double[12]; + out[0] = tx; + out[1] = ty; + out[2] = tz; + System.arraycopy(rotMatrix, 0, out, 3, 9); + return out; + } + + /** + * Get a pointer for each of the detections inside a list returned by {@link AprilTagDetectorJNI#runApriltagDetector(long, long, int, int)} + * @param ptrZarray native pointer from {@link AprilTagDetectorJNI#runApriltagDetector(long, long, int, int)} + * @return an array of native pointers to detections inside the list + */ + public static long[] getDetectionPointers(long ptrZarray) + { + org.wpilib.vision.apriltag.AprilTagDetection[] detections = AprilTagDetectionCache.getDetections(ptrZarray); + if (detections == null) return new long[0]; + + long[] ptrs = new long[detections.length]; + for (int i = 0; i < detections.length; i++) + { + ptrs[i] = AprilTagDetectionCache.cacheDetections(new org.wpilib.vision.apriltag.AprilTagDetection[]{ detections[i] }); + } + return ptrs; + } + + /** + * Frees a list of detections obtained from {@link AprilTagDetectorJNI#runApriltagDetector(long, long, int, int)} + * AND the detections themselves. (Thus, after calling this, any pointers you may have previously obtained + * from {@link #getDetectionPointers(long)} are INVALID) + * @param ptrDetections native pointer to a list of detections + */ + public static void freeDetectionList(long ptrDetections) + { + AprilTagDetectionCache.freeDetections(ptrDetections); + } + + /** + * Creates a nice decoupled java representation of the detections in the native detection list + * @param ptrDetections native pointer from {@link AprilTagDetectorJNI#runApriltagDetector(long, long, int, int)} + * @param tagSize size of the tag in meters + * @param fx lens intrinsics fx + * @param fy lens intrinsics fy + * @param cx lens intrinsics cx + * @param cy lens intrinsics cy + * @return a nice decoupled java representation of the detections in the native detection list + */ + public static ArrayList getDetections(long ptrDetections, double tagSize, double fx, double fy, double cx, double cy) + { + long[] detectionPointers = getDetectionPointers(ptrDetections); + ArrayList detections = new ArrayList<>(detectionPointers.length); + + for (long ptrDetection : detectionPointers) + { + AprilTagDetection detection = new AprilTagDetection(); + detection.id = getId(ptrDetection); + detection.hamming = getHamming(ptrDetection); + detection.decisionMargin = getDecisionMargin(ptrDetection); + double[] center = getCenterpoint(ptrDetection); + detection.center = new Point(center[0], center[1]); + double[][] corners = getCorners(ptrDetection); + + detection.corners = new Point[4]; + for (int p = 0; p < 4; p++) + { + detection.corners[p] = new Point(corners[p][0], corners[p][1]); + } + + detection.pose = new AprilTagPose(); + double[] pose = getPoseEstimate(ptrDetection, tagSize, fx, fy, cx, cy); + detection.pose.x = pose[0]; + detection.pose.y = pose[1]; + detection.pose.z = pose[2]; + + float[] rotMtxVals = new float[3*3]; + + for (int i = 0; i < 9; i++) + { + rotMtxVals[i] = (float) pose[3 + i]; + } + + detection.pose.R = new GeneralMatrixF(3, 3, rotMtxVals); + + // Free the individual detection's cache entry now that we're done with it + AprilTagDetectionCache.freeDetections(ptrDetection); + + detections.add(detection); + } + + return detections; + } + + // ------------------------------------------------------------------------- + // Private helpers + // ------------------------------------------------------------------------- + + /** + * Retrieves the single WPILib detection stored under a per-detection fake pointer. + */ + private static org.wpilib.vision.apriltag.AprilTagDetection getSingleDetection(long ptr) + { + org.wpilib.vision.apriltag.AprilTagDetection[] arr = AprilTagDetectionCache.getDetections(ptr); + if (arr == null || arr.length == 0) + throw new IllegalStateException("No detection found for pointer: " + ptr); + return arr[0]; + } + + /** + * Converts a unit quaternion (w, x, y, z) to a row-major 3x3 rotation matrix. + */ + private static double[] quaternionToRotationMatrix(double w, double x, double y, double z) + { + double[] m = new double[9]; + + m[0] = 1 - 2*(y*y + z*z); + m[1] = 2*(x*y - z*w); + m[2] = 2*(x*z + y*w); + + m[3] = 2*(x*y + z*w); + m[4] = 1 - 2*(x*x + z*z); + m[5] = 2*(y*z - x*w); + + m[6] = 2*(x*z - y*w); + m[7] = 2*(y*z + x*w); + m[8] = 1 - 2*(x*x + y*y); + + return m; + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index c01f3234..51cadaaa 100644 --- a/build.gradle +++ b/build.gradle @@ -22,13 +22,14 @@ plugins { ext { wpilibVersion = "2027.0.0-alpha-6" opencvVersion = "2027-4.13.0-3" + quickbufVersion = "1.3.3"; kotlin_version = "2.3.21" kotlinx_coroutines_version = "1.10.2" koin_version = "4.2.0" slf4j_version = "2.0.16" log4j_version = "2.24.1" - apriltag_plugin_version = "2.1.0-D" + jackson_version = "2.15.2"; papervision_version = "1.1.0" flatlaf_version = "3.5.1" From c6b140b6b5031272c025749ff43107e3d7e7b6bb Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Thu, 14 May 2026 11:16:02 -0600 Subject: [PATCH 37/40] Fix testing to use WPILib's AprilTagJNI --- .../serivesmejia/eocvsim/test/CoreTests.kt | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/EOCV-Sim/src/test/kotlin/com/github/serivesmejia/eocvsim/test/CoreTests.kt b/EOCV-Sim/src/test/kotlin/com/github/serivesmejia/eocvsim/test/CoreTests.kt index c0356b50..772b1c37 100644 --- a/EOCV-Sim/src/test/kotlin/com/github/serivesmejia/eocvsim/test/CoreTests.kt +++ b/EOCV-Sim/src/test/kotlin/com/github/serivesmejia/eocvsim/test/CoreTests.kt @@ -1,35 +1,28 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + @file:Suppress("UNUSED") package com.github.serivesmejia.eocvsim.test -import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.util.LibraryLoader import io.kotest.core.spec.style.StringSpec import org.opencv.core.Mat -import org.openftc.apriltag.AprilTagDetectorJNI +import org.wpilib.vision.apriltag.jni.AprilTagJNI -class OpenCvTest : StringSpec({ - "Loading native library" { +class LibrariesTest : StringSpec({ + "Loading Libraries" { LibraryLoader.loadLibraries() } "Creating a Mat" { Mat() } -}) - -class AprilTagsTest : StringSpec({ - "Create AprilTag detector" { - val detector = AprilTagDetectorJNI.createApriltagDetector( - AprilTagDetectorJNI.TagFamily.TAG_36h11.string, - 0f, 3 - ) - println("Created detector $detector") + "Create AprilTag Detector" { + val handle = AprilTagJNI.createDetector() + assert(handle != 0.toLong()) } -}) +}) \ No newline at end of file From 08d35f6f3676572f3b01e001f9858a7c299c7dd0 Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 19 May 2026 01:18:09 -0600 Subject: [PATCH 38/40] Move from io.github.deltacv to org.deltacv & use Jackson for TOML --- Common/build.gradle | 5 +- .../DefaultPipelineInstantiator.kt | 2 +- .../instantiator/PipelineInstantiator.kt | 2 +- .../eocvsim/util/event/EventHandler.kt | 2 +- .../eocvsim/util/io/EOCVSimFolder.kt | 2 +- .../serivesmejia/eocvsim/util/io/Lock.kt | 2 +- .../util/orchestration/Orchestrator.kt | 2 +- .../api/exception/EOCVSimApiException.kt | 10 -- .../common/image/BufferedImageRecycler.java | 2 +- .../image/DynamicBufferedImageRecycler.java | 2 +- .../deltacv/common/image/MatPoster.java | 2 +- .../pipeline}/PipelineStatisticsCalculator.kt | 4 +- .../deltacv/common/util/LoggerDelegates.kt | 2 +- .../deltacv/common/util/ParsedVersion.kt | 2 +- .../java/org/deltacv/common/util/Toml.java | 106 +++++++++++ .../deltacv/eocvsim/plugin/EOCVSimPlugin.kt | 10 +- .../deltacv/eocvsim/plugin/Folders.kt | 2 +- .../deltacv/eocvsim/plugin/api/Api.kt | 16 +- .../deltacv/eocvsim/plugin/api/ApiDisabler.kt | 2 +- .../deltacv/eocvsim/plugin/api/ConfigApi.kt | 4 +- .../eocvsim/plugin/api/DialogFactoryApi.kt | 4 +- .../deltacv/eocvsim/plugin/api/EOCVSimApi.kt | 4 +- .../deltacv/eocvsim/plugin/api/HookApi.kt | 4 +- .../eocvsim/plugin/api/InputSourceApis.kt | 4 +- .../eocvsim/plugin/api/PipelineManagerApi.kt | 6 +- .../deltacv/eocvsim/plugin/api/SwingApis.kt | 4 +- .../eocvsim/plugin/api/VariableTunerApi.kt | 8 +- .../eocvsim/plugin/api/VisualizerApi.kt | 4 +- .../plugin/api/VisualizerComponentsApi.kt | 4 +- .../plugin/exception/EOCVSimApiException.kt | 10 ++ .../eocvsim/plugin/loader/Exceptions.kt | 2 +- .../eocvsim/plugin/loader/FilePluginLoader.kt | 2 +- .../eocvsim/plugin/loader/PluginContext.kt | 23 +-- .../eocvsim/plugin/loader/PluginLoader.kt | 22 +-- .../eocvsim/plugin/loader/Providers.kt | 6 +- .../eocvsim/plugin/security/Authority.kt | 21 +-- .../plugin/security/KeyGeneratorTool.kt | 2 +- .../security/PluginSignatureVerifier.kt | 34 ++-- .../plugin/security/PluginSigningTool.kt | 2 +- .../eocvsim/sandbox/nio/SandboxFileSystem.kt | 4 +- .../restrictions/DynamicCodeRestrictions.kt | 25 ++- .../virtualreflect/jvm/JvmVirtualField.kt | 6 +- .../jvm/JvmVirtualReflectContext.kt | 6 +- .../jvm/JvmVirtualReflection.kt | 6 +- .../eocvsim/virtualreflect/jvm/Label.java | 2 +- .../virtualreflect/VirtualField.kt | 2 +- .../virtualreflect/VirtualReflectContext.kt | 4 +- .../virtualreflect/VirtualReflection.kt | 2 +- EOCV-Sim/build.gradle | 2 +- .../github/serivesmejia/eocvsim/EOCVSim.kt | 9 +- .../com/github/serivesmejia/eocvsim/Module.kt | 14 +- .../eocvsim/config/ConfigManager.kt | 2 +- .../serivesmejia/eocvsim/gui/DialogFactory.kt | 6 +- .../github/serivesmejia/eocvsim/gui/Icons.kt | 4 +- .../serivesmejia/eocvsim/gui/Visualizer.kt | 7 +- .../gui/component/tuner/ColorPicker.kt | 2 +- .../tuner/TunableFieldPanelConfig.kt | 12 +- .../tuner/TunableFieldPanelOptions.kt | 2 +- .../visualizer/InputSourceDropTarget.kt | 2 +- .../gui/component/visualizer/SidebarPanel.kt | 2 +- .../gui/component/visualizer/TopMenuBar.kt | 14 +- .../visualizer/opmode/OpModeControlsPanel.kt | 4 +- .../visualizer/opmode/OpModeSelectorPanel.kt | 4 +- .../serivesmejia/eocvsim/gui/dialog/About.kt | 2 +- .../eocvsim/gui/dialog/PluginOutput.kt | 22 +-- .../gui/dialog/source/CreateImageSource.kt | 2 +- .../gui/dialog/source/CreateVideoSource.kt | 2 +- .../serivesmejia/eocvsim/gui/util/GuiUtil.kt | 4 +- .../eocvsim/gui/util/ThreadedMatPoster.java | 2 +- .../eocvsim/input/InputSourceInitializer.kt | 2 +- .../eocvsim/input/InputSourceManager.kt | 2 +- .../eocvsim/input/source/CameraSource.kt | 2 +- .../eocvsim/input/source/HttpSource.kt | 2 +- .../eocvsim/output/RecordingManager.kt | 2 +- .../eocvsim/output/VideoRecordingSession.kt | 6 +- .../eocvsim/pipeline/PipelineManager.kt | 22 +-- .../compiled/CompiledPipelineManager.kt | 2 +- .../pipeline/compiled/PipelineClassLoader.kt | 4 +- .../pipeline/compiled/PipelineCompiler.kt | 2 +- .../compiled/PipelineStandardFileManager.kt | 2 +- .../processor/ProcessorInstantiator.kt | 2 +- .../pipeline/util/PipelineExceptionTracker.kt | 2 +- .../eocvsim/pipeline/util/PipelineSnapshot.kt | 10 +- .../eocvsim/plugin/api/impl/ConfigApiImpl.kt | 4 +- .../plugin/api/impl/DialogFactoryApiImpl.kt | 10 +- .../eocvsim/plugin/api/impl/EOCVSimApiImpl.kt | 16 +- .../api/impl/EventHandlerHookApiImpl.kt | 16 +- .../plugin/api/impl/InputSourceApisImpl.kt | 6 +- .../plugin/api/impl/PipelineManagerApiImpl.kt | 8 +- .../plugin/api/impl/SimpleHookApiImpl.kt | 4 +- .../eocvsim/plugin/api/impl/SwingApisImpl.kt | 8 +- .../plugin/api/impl/VariableTunerApiImpl.kt | 19 +- .../plugin/api/impl/VisualizerApiImpl.kt | 6 +- .../api/impl/VisualizerComponentsApiImpl.kt | 10 +- .../output/VisualPluginOutputHandler.kt | 2 +- .../eocvsim/tuner/TunableField.kt | 2 +- .../eocvsim/tuner/TunableFieldRegistry.kt | 2 +- .../eocvsim/tuner/TunerManager.kt | 8 +- .../eocvsim/tuner/field/BooleanField.kt | 2 +- .../eocvsim/tuner/field/EnumField.kt | 2 +- .../eocvsim/tuner/field/NumericField.kt | 2 +- .../eocvsim/tuner/field/StringField.kt | 2 +- .../eocvsim/tuner/field/cv/PointField.kt | 2 +- .../eocvsim/tuner/field/cv/RectField.kt | 2 +- .../eocvsim/tuner/field/cv/ScalarField.kt | 2 +- .../tuner/field/numeric/DoubleField.kt | 2 +- .../eocvsim/tuner/field/numeric/FloatField.kt | 2 +- .../tuner/field/numeric/IntegerField.kt | 2 +- .../eocvsim/tuner/field/numeric/LongField.kt | 2 +- .../eocvsim/util/ClasspathScan.kt | 4 +- .../eocvsim/util/compiler/CompilerProvider.kt | 2 +- .../util/exception/handling/CrashReport.kt | 2 +- .../EOCVSimUncaughtExceptionHandler.kt | 2 +- .../eocvsim/util/io/FileWatcher.kt | 2 +- .../eocvsim/workspace/WorkspaceManager.kt | 2 +- .../workspace/config/WorkspaceConfigLoader.kt | 2 +- .../eocvsim/workspace/util/VSCodeLauncher.kt | 2 +- .../workspace/util/WorkspaceTemplate.kt | 2 +- .../util/template/DefaultWorkspaceTemplate.kt | 2 +- .../util/template/GradleWorkspaceTemplate.kt | 2 +- .../eventloop/opmode/OpModePipelineHandler.kt | 4 +- .../restrictions/MethodCallByteCodeChecker.kt | 70 -------- .../eocvsim/gui/dialog/SuperAccessRequest.kt | 12 +- .../eocvsim/input/VisionInputSource.kt | 12 +- .../input/VisionInputSourceProvider.kt | 12 +- .../input/control/CameraSourceControlMap.kt | 14 +- .../control/CameraSourceExposureControl.kt | 12 +- .../StreamableOpenCvPipelineInstantiator.kt | 8 +- .../plugin/loader/EmbeddedPluginLoader.kt | 22 +-- .../plugin/loader/FilePluginLoaderImpl.kt | 60 ++++--- .../plugin/loader/PluginClassLoader.kt | 14 +- .../eocvsim/plugin/loader/PluginManager.kt | 42 +++-- .../repository/PluginRepositoryManager.kt | 20 +-- .../PluginRepositoryUpdateChecker.kt | 6 +- .../security/superaccess/SuperAccessDaemon.kt | 57 ++++-- .../superaccess/SuperAccessDaemonClient.kt | 44 +++-- .../restrictions/MethodCallByteCodeChecker.kt | 164 ++++++++++++++++++ EOCV-Sim/src/main/resources/contributors.txt | 4 +- .../src/main/resources/opensourcelibs.txt | 4 +- Vision/build.gradle | 2 +- .../eventloop/opmode/LinearOpMode.java | 4 +- .../robotcore/eventloop/opmode/OpMode.java | 8 +- .../robotcore/hardware/HardwareMap.java | 4 +- .../pipeline/StreamableOpenCvPipeline.java | 4 +- .../deltacv/eocvsim/stream/ImageStreamer.kt | 2 +- .../external/FrameReceiverOpenCvCamera.java | 6 +- .../external}/external/PipelineRenderHook.kt | 2 +- .../vision/external/gui/component/ImageX.java | 6 +- .../vision/external/gui}/gui/SkiaPanel.kt | 2 +- .../external/gui}/gui/SwingOpenCvViewport.kt | 4 +- .../vision/external/gui/util/ImgUtil.java | 2 +- .../external/source/CameraControlMap.java | 2 +- .../vision/external/source/FrameReceiver.java | 2 +- .../source/ThreadVisionSourceProvider.java | 2 +- .../source/ViewportVisionSourceProvider.java | 2 +- .../vision/external/source/VisionSource.java | 2 +- .../external/source/VisionSourceBase.java | 6 +- .../external/source/VisionSourceProvider.java | 2 +- .../vision/external/util/extension/CvExt.kt | 2 +- .../vision/external/util}/util/CvUtil.java | 4 +- .../external/util}/util/FrameQueue.java | 2 +- .../external/util}/util/ThrowableHandler.java | 2 +- .../external/util}/util/Timestamped.java | 2 +- .../deltacv/vision/internal/opmode/Enums.kt | 2 +- .../vision/internal/opmode/OpModeNotifier.kt | 2 +- .../RedirectToOpModeThrowableHandler.kt | 4 +- .../source/ftc/SourcedCameraName.java | 4 +- .../source/ftc/SourcedCameraNameImpl.java | 4 +- .../ftc/vision/VisionPortal.java | 4 +- .../ftc/vision/VisionPortalImpl.java | 2 +- .../openftc/easyopencv/OpenCvCameraBase.java | 4 +- .../SourcedOpenCvCameraFactoryImpl.java | 10 +- 172 files changed, 847 insertions(+), 599 deletions(-) delete mode 100644 Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/exception/EOCVSimApiException.kt rename Common/src/main/java/{io/github => org}/deltacv/common/image/BufferedImageRecycler.java (99%) rename Common/src/main/java/{io/github => org}/deltacv/common/image/DynamicBufferedImageRecycler.java (98%) rename Common/src/main/java/{io/github => org}/deltacv/common/image/MatPoster.java (93%) rename Common/src/main/java/{io/github/deltacv/common/pipeline/util => org/deltacv/common/pipeline}/PipelineStatisticsCalculator.kt (98%) rename Common/src/main/java/{io/github => org}/deltacv/common/util/LoggerDelegates.kt (87%) rename Common/src/main/java/{io/github => org}/deltacv/common/util/ParsedVersion.kt (97%) create mode 100644 Common/src/main/java/org/deltacv/common/util/Toml.java rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/EOCVSimPlugin.kt (90%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/Folders.kt (92%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/api/Api.kt (95%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/api/ApiDisabler.kt (87%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/api/ConfigApi.kt (91%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/api/DialogFactoryApi.kt (94%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/api/EOCVSimApi.kt (88%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/api/HookApi.kt (92%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/api/InputSourceApis.kt (94%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/api/PipelineManagerApi.kt (94%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/api/SwingApis.kt (93%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/api/VariableTunerApi.kt (88%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/api/VisualizerApi.kt (94%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/api/VisualizerComponentsApi.kt (95%) create mode 100644 Common/src/main/java/org/deltacv/eocvsim/plugin/exception/EOCVSimApiException.kt rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/loader/Exceptions.kt (86%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/loader/FilePluginLoader.kt (90%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/loader/PluginContext.kt (94%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/loader/PluginLoader.kt (90%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/loader/Providers.kt (83%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/security/Authority.kt (92%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt (93%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt (83%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/security/PluginSigningTool.kt (96%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/sandbox/nio/SandboxFileSystem.kt (97%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/sandbox/restrictions/DynamicCodeRestrictions.kt (93%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualField.kt (88%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflectContext.kt (86%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflection.kt (79%) rename Common/src/main/java/{io/github => org}/deltacv/eocvsim/virtualreflect/jvm/Label.java (89%) rename Common/src/main/java/{io/github/deltacv/eocvsim => org/deltacv/eocvsim/virtualreflect}/virtualreflect/VirtualField.kt (90%) rename Common/src/main/java/{io/github/deltacv/eocvsim => org/deltacv/eocvsim/virtualreflect}/virtualreflect/VirtualReflectContext.kt (88%) rename Common/src/main/java/{io/github/deltacv/eocvsim => org/deltacv/eocvsim/virtualreflect}/virtualreflect/VirtualReflection.kt (87%) delete mode 100644 EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/MethodCallByteCodeChecker.kt rename EOCV-Sim/src/main/java/{io/github => org}/deltacv/eocvsim/gui/dialog/SuperAccessRequest.kt (96%) rename EOCV-Sim/src/main/java/{io/github => org}/deltacv/eocvsim/input/VisionInputSource.kt (79%) rename EOCV-Sim/src/main/java/{io/github => org}/deltacv/eocvsim/input/VisionInputSourceProvider.kt (86%) rename EOCV-Sim/src/main/java/{io/github => org}/deltacv/eocvsim/input/control/CameraSourceControlMap.kt (83%) rename EOCV-Sim/src/main/java/{io/github => org}/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt (95%) rename EOCV-Sim/src/main/java/{io/github => org}/deltacv/eocvsim/pipeline/StreamableOpenCvPipelineInstantiator.kt (83%) rename EOCV-Sim/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/loader/EmbeddedPluginLoader.kt (89%) rename EOCV-Sim/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt (79%) rename EOCV-Sim/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/loader/PluginClassLoader.kt (91%) rename EOCV-Sim/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/loader/PluginManager.kt (89%) rename EOCV-Sim/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt (95%) rename EOCV-Sim/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/repository/PluginRepositoryUpdateChecker.kt (94%) rename EOCV-Sim/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemon.kt (81%) rename EOCV-Sim/src/main/java/{io/github => org}/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemonClient.kt (86%) create mode 100644 EOCV-Sim/src/main/java/org/deltacv/eocvsim/sandbox/restrictions/MethodCallByteCodeChecker.kt rename Vision/src/main/java/{io/github => org}/deltacv/eocvsim/pipeline/StreamableOpenCvPipeline.java (93%) rename Vision/src/main/java/{io/github => org}/deltacv/eocvsim/stream/ImageStreamer.kt (82%) rename Vision/src/main/java/{io/github/deltacv/vision => org/deltacv/vision/external}/external/FrameReceiverOpenCvCamera.java (93%) rename Vision/src/main/java/{io/github/deltacv/vision => org/deltacv/vision/external}/external/PipelineRenderHook.kt (94%) rename Vision/src/main/java/{io/github => org}/deltacv/vision/external/gui/component/ImageX.java (87%) rename Vision/src/main/java/{io/github/deltacv/vision/external => org/deltacv/vision/external/gui}/gui/SkiaPanel.kt (91%) rename Vision/src/main/java/{io/github/deltacv/vision/external => org/deltacv/vision/external/gui}/gui/SwingOpenCvViewport.kt (99%) rename Vision/src/main/java/{io/github => org}/deltacv/vision/external/gui/util/ImgUtil.java (89%) rename Vision/src/main/java/{io/github => org}/deltacv/vision/external/source/CameraControlMap.java (82%) rename Vision/src/main/java/{io/github => org}/deltacv/vision/external/source/FrameReceiver.java (80%) rename Vision/src/main/java/{io/github => org}/deltacv/vision/external/source/ThreadVisionSourceProvider.java (89%) rename Vision/src/main/java/{io/github => org}/deltacv/vision/external/source/ViewportVisionSourceProvider.java (80%) rename Vision/src/main/java/{io/github => org}/deltacv/vision/external/source/VisionSource.java (82%) rename Vision/src/main/java/{io/github => org}/deltacv/vision/external/source/VisionSourceBase.java (95%) rename Vision/src/main/java/{io/github => org}/deltacv/vision/external/source/VisionSourceProvider.java (73%) rename Vision/src/main/java/{io/github => org}/deltacv/vision/external/util/extension/CvExt.kt (89%) rename Vision/src/main/java/{io/github/deltacv/vision/external => org/deltacv/vision/external/util}/util/CvUtil.java (94%) rename Vision/src/main/java/{io/github/deltacv/vision/external => org/deltacv/vision/external/util}/util/FrameQueue.java (92%) rename Vision/src/main/java/{io/github/deltacv/vision/external => org/deltacv/vision/external/util}/util/ThrowableHandler.java (73%) rename Vision/src/main/java/{io/github/deltacv/vision/external => org/deltacv/vision/external/util}/util/Timestamped.java (85%) rename Vision/src/main/java/{io/github => org}/deltacv/vision/internal/opmode/Enums.kt (79%) rename Vision/src/main/java/{io/github => org}/deltacv/vision/internal/opmode/OpModeNotifier.kt (94%) rename Vision/src/main/java/{io/github => org}/deltacv/vision/internal/opmode/RedirectToOpModeThrowableHandler.kt (67%) rename Vision/src/main/java/{io/github => org}/deltacv/vision/internal/source/ftc/SourcedCameraName.java (83%) rename Vision/src/main/java/{io/github => org}/deltacv/vision/internal/source/ftc/SourcedCameraNameImpl.java (87%) diff --git a/Common/build.gradle b/Common/build.gradle index 435516a0..c2486375 100644 --- a/Common/build.gradle +++ b/Common/build.gradle @@ -18,9 +18,10 @@ wpilibTools.deps.wpilibVersion = wpilibVersion dependencies { // WPILib OpenCV replaces openpnp - api wpilibTools.deps.wpilibOpenCvJava(opencvVersion) + compileOnly wpilibTools.deps.wpilibOpenCvJava(opencvVersion) - implementation "com.moandjiezana.toml:toml4j:$toml4j_version" + // Replace toml4j with Jackson TOML dataformat + implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-toml:$jackson_version" implementation "info.picocli:picocli:$picocli_version" implementation "org.slf4j:slf4j-api:$slf4j_version" diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/DefaultPipelineInstantiator.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/DefaultPipelineInstantiator.kt index 7f3050a4..1a529a1b 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/DefaultPipelineInstantiator.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/DefaultPipelineInstantiator.kt @@ -5,7 +5,7 @@ package com.github.serivesmejia.eocvsim.pipeline.instantiator -import io.github.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflection +import org.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflection import org.firstinspires.ftc.robotcore.external.Telemetry import org.openftc.easyopencv.OpenCvPipeline diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/PipelineInstantiator.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/PipelineInstantiator.kt index 04bc6a73..3c92b6fa 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/PipelineInstantiator.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/PipelineInstantiator.kt @@ -5,7 +5,7 @@ package com.github.serivesmejia.eocvsim.pipeline.instantiator -import io.github.deltacv.eocvsim.virtualreflect.VirtualReflection +import org.deltacv.eocvsim.virtualreflect.VirtualReflection import org.firstinspires.ftc.robotcore.external.Telemetry import org.openftc.easyopencv.OpenCvPipeline diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventHandler.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventHandler.kt index 88f2639e..000e4a05 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventHandler.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/event/EventHandler.kt @@ -5,7 +5,7 @@ package com.github.serivesmejia.eocvsim.util.event -import io.github.deltacv.common.util.loggerOf +import org.deltacv.common.util.loggerOf import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/EOCVSimFolder.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/EOCVSimFolder.kt index 5a107ce3..ff1ead47 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/EOCVSimFolder.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/EOCVSimFolder.kt @@ -6,7 +6,7 @@ package com.github.serivesmejia.eocvsim.util.io import com.github.serivesmejia.eocvsim.util.extension.appData -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import java.io.File /** diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/Lock.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/Lock.kt index 67923ab4..fecf254b 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/Lock.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/io/Lock.kt @@ -5,7 +5,7 @@ package com.github.serivesmejia.eocvsim.util.io -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import java.io.File import java.io.RandomAccessFile import java.nio.channels.FileLock diff --git a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrator.kt b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrator.kt index 35b66eea..9dd7113c 100644 --- a/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrator.kt +++ b/Common/src/main/java/com/github/serivesmejia/eocvsim/util/orchestration/Orchestrator.kt @@ -7,7 +7,7 @@ package com.github.serivesmejia.eocvsim.util.orchestration -import io.github.deltacv.common.util.loggerOf +import org.deltacv.common.util.loggerOf import kotlinx.coroutines.* import kotlin.reflect.KClass diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/exception/EOCVSimApiException.kt b/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/exception/EOCVSimApiException.kt deleted file mode 100644 index 050d63c2..00000000 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/exception/EOCVSimApiException.kt +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - -package io.github.deltacv.eocvsim.plugin.api.exception - -import io.github.deltacv.eocvsim.plugin.api.Api - -class EOCVSimApiException(message: String, val api: Api) : RuntimeException("Exception thrown by API ${api::class.simpleName} $message") diff --git a/Common/src/main/java/io/github/deltacv/common/image/BufferedImageRecycler.java b/Common/src/main/java/org/deltacv/common/image/BufferedImageRecycler.java similarity index 99% rename from Common/src/main/java/io/github/deltacv/common/image/BufferedImageRecycler.java rename to Common/src/main/java/org/deltacv/common/image/BufferedImageRecycler.java index cea14acb..4999a751 100644 --- a/Common/src/main/java/io/github/deltacv/common/image/BufferedImageRecycler.java +++ b/Common/src/main/java/org/deltacv/common/image/BufferedImageRecycler.java @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.common.image; +package org.deltacv.common.image; import java.awt.*; import java.awt.image.BufferedImage; diff --git a/Common/src/main/java/io/github/deltacv/common/image/DynamicBufferedImageRecycler.java b/Common/src/main/java/org/deltacv/common/image/DynamicBufferedImageRecycler.java similarity index 98% rename from Common/src/main/java/io/github/deltacv/common/image/DynamicBufferedImageRecycler.java rename to Common/src/main/java/org/deltacv/common/image/DynamicBufferedImageRecycler.java index 41c4219b..c4275be2 100644 --- a/Common/src/main/java/io/github/deltacv/common/image/DynamicBufferedImageRecycler.java +++ b/Common/src/main/java/org/deltacv/common/image/DynamicBufferedImageRecycler.java @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.common.image; +package org.deltacv.common.image; import java.awt.*; import java.awt.image.BufferedImage; diff --git a/Common/src/main/java/io/github/deltacv/common/image/MatPoster.java b/Common/src/main/java/org/deltacv/common/image/MatPoster.java similarity index 93% rename from Common/src/main/java/io/github/deltacv/common/image/MatPoster.java rename to Common/src/main/java/org/deltacv/common/image/MatPoster.java index 295d638e..763d3b83 100644 --- a/Common/src/main/java/io/github/deltacv/common/image/MatPoster.java +++ b/Common/src/main/java/org/deltacv/common/image/MatPoster.java @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.common.image; +package org.deltacv.common.image; import org.opencv.core.Mat; diff --git a/Common/src/main/java/io/github/deltacv/common/pipeline/util/PipelineStatisticsCalculator.kt b/Common/src/main/java/org/deltacv/common/pipeline/PipelineStatisticsCalculator.kt similarity index 98% rename from Common/src/main/java/io/github/deltacv/common/pipeline/util/PipelineStatisticsCalculator.kt rename to Common/src/main/java/org/deltacv/common/pipeline/PipelineStatisticsCalculator.kt index 9ac50466..aa17fffd 100644 --- a/Common/src/main/java/io/github/deltacv/common/pipeline/util/PipelineStatisticsCalculator.kt +++ b/Common/src/main/java/org/deltacv/common/pipeline/PipelineStatisticsCalculator.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.common.pipeline.util +package org.deltacv.common.pipeline import com.qualcomm.robotcore.util.ElapsedTime import com.qualcomm.robotcore.util.MovingStatistics @@ -100,4 +100,4 @@ class PipelineStatisticsCalculator { avgOverheadTime = avgTotalFrameTime - avgPipelineTime } -} +} diff --git a/Common/src/main/java/io/github/deltacv/common/util/LoggerDelegates.kt b/Common/src/main/java/org/deltacv/common/util/LoggerDelegates.kt similarity index 87% rename from Common/src/main/java/io/github/deltacv/common/util/LoggerDelegates.kt rename to Common/src/main/java/org/deltacv/common/util/LoggerDelegates.kt index e409c2c1..07ca8bf2 100644 --- a/Common/src/main/java/io/github/deltacv/common/util/LoggerDelegates.kt +++ b/Common/src/main/java/org/deltacv/common/util/LoggerDelegates.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.common.util +package org.deltacv.common.util import org.slf4j.LoggerFactory import kotlin.reflect.KClass diff --git a/Common/src/main/java/io/github/deltacv/common/util/ParsedVersion.kt b/Common/src/main/java/org/deltacv/common/util/ParsedVersion.kt similarity index 97% rename from Common/src/main/java/io/github/deltacv/common/util/ParsedVersion.kt rename to Common/src/main/java/org/deltacv/common/util/ParsedVersion.kt index 7eec93bc..06ddcc5f 100644 --- a/Common/src/main/java/io/github/deltacv/common/util/ParsedVersion.kt +++ b/Common/src/main/java/org/deltacv/common/util/ParsedVersion.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.common.util +package org.deltacv.common.util /** * ParsedVersion class to parse and compare versions diff --git a/Common/src/main/java/org/deltacv/common/util/Toml.java b/Common/src/main/java/org/deltacv/common/util/Toml.java new file mode 100644 index 00000000..0b5dc860 --- /dev/null +++ b/Common/src/main/java/org/deltacv/common/util/Toml.java @@ -0,0 +1,106 @@ +package org.deltacv.common.util; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.toml.TomlFactory; + +import java.io.File; +import java.io.InputStream; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * Minimal TOML helper backed by Jackson TOML. Provides a thin API + * compatible with the previous toml4j usages in this project: + * new Toml().read(file) -> returns Toml + * getString/getLong/getBoolean/getList/getTable/toMap + */ +public class Toml { + + private final Map data; + + private static final ObjectMapper MAPPER = new ObjectMapper(new TomlFactory()); + + public Toml() { + this.data = Collections.emptyMap(); + } + + private Toml(Map data) { + this.data = data == null ? Collections.emptyMap() : data; + } + + public Toml read(File f) { + try (InputStream in = java.nio.file.Files.newInputStream(f.toPath())) { + Map m = MAPPER.readValue(in, new TypeReference>() {}); + return new Toml(m); + } catch (Exception e) { + throw new RuntimeException("Failed to read TOML file: " + f.getAbsolutePath(), e); + } + } + + public Toml read(InputStream in) { + try { + Map m = MAPPER.readValue(in, new TypeReference>() {}); + return new Toml(m); + } catch (Exception e) { + throw new RuntimeException("Failed to read TOML from stream", e); + } + } + + @SuppressWarnings("unchecked") + public String getString(String key) { + Object v = data.get(key); + return v == null ? null : v.toString(); + } + + public String getString(String key, String def) { + String s = getString(key); + return s == null ? def : s; + } + + public boolean contains(String key) { + return data != null && data.containsKey(key); + } + + @SuppressWarnings("unchecked") + public Long getLong(String key) { + Object v = data.get(key); + if (v == null) return null; + if (v instanceof Number) return ((Number) v).longValue(); + try { return Long.parseLong(v.toString()); } catch (Exception e) { return null; } + } + + @SuppressWarnings("unchecked") + public Boolean getBoolean(String key) { + Object v = data.get(key); + if (v == null) return null; + if (v instanceof Boolean) return (Boolean) v; + return Boolean.parseBoolean(v.toString()); + } + + public boolean getBoolean(String key, boolean def) { + Boolean b = getBoolean(key); + return b == null ? def : b; + } + + @SuppressWarnings("unchecked") + public List getList(String key) { + Object v = data.get(key); + if (v instanceof List) return (List) v; + return null; + } + + @SuppressWarnings("unchecked") + public Toml getTable(String key) { + Object v = data.get(key); + if (v instanceof Map) return new Toml((Map) v); + return null; + } + + public Map toMap() { + return data == null ? Collections.emptyMap() : data; + } +} + + diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/EOCVSimPlugin.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/EOCVSimPlugin.kt similarity index 90% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/EOCVSimPlugin.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/EOCVSimPlugin.kt index 827bae45..7e41e63c 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/EOCVSimPlugin.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/EOCVSimPlugin.kt @@ -3,10 +3,10 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin +package org.deltacv.eocvsim.plugin -import io.github.deltacv.common.util.loggerForThis -import io.github.deltacv.eocvsim.plugin.loader.PluginContext +import org.deltacv.common.util.loggerForThis +import org.deltacv.eocvsim.plugin.loader.PluginContext /** * Base class for all EOCV-Sim plugins. @@ -54,7 +54,7 @@ abstract class EOCVSimPlugin { * Without super access, the plugin is restricted to its own isolated * virtual filesystem and cannot access host or other plugin data. * - * @see io.github.deltacv.eocvsim.sandbox.nio.SandboxFileSystem + * @see org.deltacv.eocvsim.sandbox.nio.SandboxFileSystem */ val fileSystem get() = context.fileSystem @@ -65,7 +65,7 @@ abstract class EOCVSimPlugin { * incorporated into the runtime classpath (for example, whether it was * loaded from a local file or a Maven repository). * - * @see io.github.deltacv.eocvsim.plugin.loader.PluginSource + * @see org.deltacv.eocvsim.plugin.loader.PluginSource */ val pluginSource get() = context.pluginSource diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/Folders.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/Folders.kt similarity index 92% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/Folders.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/Folders.kt index 00ad26b1..3a60dbf1 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/Folders.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/Folders.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin +package org.deltacv.eocvsim.plugin import com.github.serivesmejia.eocvsim.util.extension.plus import com.github.serivesmejia.eocvsim.util.io.EOCVSimFolder diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/Api.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/Api.kt similarity index 95% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/Api.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/api/Api.kt index 0bd8292d..ca54fd08 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/Api.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/Api.kt @@ -1,12 +1,12 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - -package io.github.deltacv.eocvsim.plugin.api +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.deltacv.eocvsim.plugin.api -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.exception.EOCVSimApiException +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.exception.EOCVSimApiException import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/ApiDisabler.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/ApiDisabler.kt similarity index 87% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/ApiDisabler.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/api/ApiDisabler.kt index 731bbe81..73f73a6d 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/ApiDisabler.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/ApiDisabler.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.api +package org.deltacv.eocvsim.plugin.api /** * Utility object to disable APIs diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/ConfigApi.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/ConfigApi.kt similarity index 91% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/ConfigApi.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/api/ConfigApi.kt index a00e4e43..0ac68e57 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/ConfigApi.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/ConfigApi.kt @@ -3,9 +3,9 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.api +package org.deltacv.eocvsim.plugin.api -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.EOCVSimPlugin /** * Base API for managing plugin configuration flags. diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/DialogFactoryApi.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/DialogFactoryApi.kt similarity index 94% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/DialogFactoryApi.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/api/DialogFactoryApi.kt index 5739eb6e..123a5423 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/DialogFactoryApi.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/DialogFactoryApi.kt @@ -3,9 +3,9 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.api +package org.deltacv.eocvsim.plugin.api -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.EOCVSimPlugin import java.io.File import javax.swing.filechooser.FileFilter diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/EOCVSimApi.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/EOCVSimApi.kt similarity index 88% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/EOCVSimApi.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/api/EOCVSimApi.kt index 7190ba76..bbf58de7 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/EOCVSimApi.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/EOCVSimApi.kt @@ -3,9 +3,9 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.api +package org.deltacv.eocvsim.plugin.api -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.EOCVSimPlugin /** * Root API entry point exposed to plugins by EOCV-Sim. diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/HookApi.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/HookApi.kt similarity index 92% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/HookApi.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/api/HookApi.kt index 0c3dcbcd..74b48d69 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/HookApi.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/HookApi.kt @@ -3,9 +3,9 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.api +package org.deltacv.eocvsim.plugin.api -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.EOCVSimPlugin /** * API for registering and executing lifecycle hooks. diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/InputSourceApis.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/InputSourceApis.kt similarity index 94% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/InputSourceApis.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/api/InputSourceApis.kt index 5f10138a..4f49a9e6 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/InputSourceApis.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/InputSourceApis.kt @@ -3,9 +3,9 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.api +package org.deltacv.eocvsim.plugin.api -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.EOCVSimPlugin import org.opencv.core.Size /** diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/PipelineManagerApi.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/PipelineManagerApi.kt similarity index 94% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/PipelineManagerApi.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/api/PipelineManagerApi.kt index 4166e095..4c6679ef 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/PipelineManagerApi.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/PipelineManagerApi.kt @@ -3,10 +3,10 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.api +package org.deltacv.eocvsim.plugin.api -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.virtualreflect.VirtualReflection +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.virtualreflect.VirtualReflection import org.firstinspires.ftc.robotcore.external.Telemetry import org.openftc.easyopencv.OpenCvPipeline diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/SwingApis.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/SwingApis.kt similarity index 93% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/SwingApis.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/api/SwingApis.kt index f917ea36..a373f6cf 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/SwingApis.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/SwingApis.kt @@ -3,9 +3,9 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.api +package org.deltacv.eocvsim.plugin.api -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.EOCVSimPlugin import java.io.File import javax.swing.JMenuItem import javax.swing.filechooser.FileFilter diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VariableTunerApi.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/VariableTunerApi.kt similarity index 88% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VariableTunerApi.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/api/VariableTunerApi.kt index 53289335..bba57edf 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VariableTunerApi.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/VariableTunerApi.kt @@ -3,10 +3,10 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.api +package org.deltacv.eocvsim.plugin.api -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.virtualreflect.VirtualField /** * Represents a single tunable field exposed by the variable tuner. * @@ -63,7 +63,7 @@ abstract class VariableTunerApi(owner: EOCVSimPlugin) : Api(owner) { * Retrieves an existing tunable field by its label, specified * by [VirtualField.label]. * - * @see [io.github.deltacv.eocvsim.virtualreflect.jvm.Label] + * @see [org.deltacv.eocvsim.virtualreflect.jvm.Label] * @param label label identifying the tunable field * @return the matching tunable field, or null if none exists */ diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerApi.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/VisualizerApi.kt similarity index 94% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerApi.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/api/VisualizerApi.kt index aa6edea8..72fe15e1 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerApi.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/VisualizerApi.kt @@ -3,9 +3,9 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.api +package org.deltacv.eocvsim.plugin.api -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.EOCVSimPlugin import javax.swing.JFrame import javax.swing.JPanel diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerComponentsApi.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/VisualizerComponentsApi.kt similarity index 95% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerComponentsApi.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/api/VisualizerComponentsApi.kt index cd0716a5..ed259f25 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/api/VisualizerComponentsApi.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/api/VisualizerComponentsApi.kt @@ -3,9 +3,9 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.api +package org.deltacv.eocvsim.plugin.api -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.EOCVSimPlugin import javax.swing.JPanel /** diff --git a/Common/src/main/java/org/deltacv/eocvsim/plugin/exception/EOCVSimApiException.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/exception/EOCVSimApiException.kt new file mode 100644 index 00000000..a5d4173c --- /dev/null +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/exception/EOCVSimApiException.kt @@ -0,0 +1,10 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.deltacv.eocvsim.plugin.exception + +import org.deltacv.eocvsim.plugin.api.Api + +class EOCVSimApiException(message: String, val api: Api) : RuntimeException("Exception thrown by API ${api::class.simpleName}: $message") \ No newline at end of file diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Exceptions.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/loader/Exceptions.kt similarity index 86% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Exceptions.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/loader/Exceptions.kt index 7400cc6e..deaae82e 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Exceptions.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/loader/Exceptions.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.loader +package org.deltacv.eocvsim.plugin.loader import kotlin.RuntimeException diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoader.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/loader/FilePluginLoader.kt similarity index 90% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoader.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/loader/FilePluginLoader.kt index 551e30d3..59702bb2 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoader.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/loader/FilePluginLoader.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.loader +package org.deltacv.eocvsim.plugin.loader import java.io.File diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginContext.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/loader/PluginContext.kt similarity index 94% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginContext.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/loader/PluginContext.kt index 0b62ec5d..b8bc837c 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginContext.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/loader/PluginContext.kt @@ -1,16 +1,16 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + @file:Suppress("unused") -package io.github.deltacv.eocvsim.plugin.loader +package org.deltacv.eocvsim.plugin.loader -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.loader.PluginContext.Companion.clearContext -import io.github.deltacv.eocvsim.plugin.loader.PluginContext.Companion.pushContext -import io.github.deltacv.eocvsim.sandbox.nio.SandboxFileSystem +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.loader.PluginContext.Companion.clearContext +import org.deltacv.eocvsim.plugin.loader.PluginContext.Companion.pushContext +import org.deltacv.eocvsim.sandbox.nio.SandboxFileSystem import java.util.* /** @@ -67,16 +67,19 @@ constructor( * in order of precedence: * * 1. **Cached plugin association** + * * Once a plugin instance has been created, the resolved context is cached * using weak references to avoid repeated resolution and to allow garbage * collection when a plugin is unloaded. * * 2. **ClassLoader-provided context** + * * If the plugin’s defining class loader implements [PluginContextHolder], * the context is resolved directly from it. This is the preferred and most * efficient resolution path during normal runtime execution. * * 3. **Thread-local context (highest priority)** + * * During plugin instantiation, the loader temporarily binds the active * `PluginContext` to the current thread. This guarantees that constructor * code and field initializers can access loader services even when no class diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginLoader.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/loader/PluginLoader.kt similarity index 90% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginLoader.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/loader/PluginLoader.kt index a8e9a9ab..2b44bb08 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginLoader.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/loader/PluginLoader.kt @@ -1,16 +1,16 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - -package io.github.deltacv.eocvsim.plugin.loader +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.deltacv.eocvsim.plugin.loader import com.github.serivesmejia.eocvsim.util.extension.hashString -import com.moandjiezana.toml.Toml -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.EOCVSimApi -import io.github.deltacv.eocvsim.plugin.security.PluginSignature -import io.github.deltacv.eocvsim.sandbox.nio.SandboxFileSystem +import org.deltacv.common.util.Toml +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.api.EOCVSimApi +import org.deltacv.eocvsim.plugin.security.PluginSignature +import org.deltacv.eocvsim.sandbox.nio.SandboxFileSystem import java.io.File enum class PluginSource { diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Providers.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/loader/Providers.kt similarity index 83% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Providers.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/loader/Providers.kt index 9085325d..2db78126 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/loader/Providers.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/loader/Providers.kt @@ -3,10 +3,10 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.loader +package org.deltacv.eocvsim.plugin.loader -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.EOCVSimApi +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.api.EOCVSimApi /** * Marks an object as carrying a [PluginContext]. diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/security/Authority.kt similarity index 92% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/security/Authority.kt index f707338c..e62fbb45 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/Authority.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/security/Authority.kt @@ -1,14 +1,15 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - -package io.github.deltacv.eocvsim.plugin.security +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.deltacv.eocvsim.plugin.security import com.github.serivesmejia.eocvsim.util.extension.plus import com.github.serivesmejia.eocvsim.util.io.LockFile -import io.github.deltacv.common.util.loggerForThis -import io.github.deltacv.eocvsim.plugin.PLUGIN_CACHING_FOLDER +import org.deltacv.common.util.loggerForThis +import org.deltacv.common.util.Toml +import org.deltacv.eocvsim.plugin.PLUGIN_CACHING_FOLDER import java.io.File import java.net.URI import java.security.KeyFactory @@ -60,7 +61,7 @@ object AuthorityFetcher { // Load authorities from file if it exists if (AUTHORITIES_FILE.exists() && tryLockAuthoritiesFile()) { try { - val authoritiesToml = com.moandjiezana.toml.Toml().read(AUTHORITIES_FILE) + val authoritiesToml = Toml().read(AUTHORITIES_FILE) val timestamp = authoritiesToml.getLong("timestamp") if(System.currentTimeMillis() - timestamp > TTL_DURATION_MS) { @@ -123,7 +124,7 @@ object AuthorityFetcher { AUTHORITIES_FILE.writeText("timestamp = $currentTime\n") } - val authoritiesToml = com.moandjiezana.toml.Toml().read(AUTHORITIES_FILE) + val authoritiesToml = Toml().read(AUTHORITIES_FILE) val timestamp = authoritiesToml.getLong("timestamp") if(timestamp != null && currentTime - timestamp > TTL_DURATION_MS) { diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt similarity index 93% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt index f21e8103..c1e795e5 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/security/KeyGeneratorTool.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.security +package org.deltacv.eocvsim.plugin.security import java.io.File import java.io.FileWriter diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt similarity index 83% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt index caa79e46..570e0faf 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt @@ -1,14 +1,14 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - -package io.github.deltacv.eocvsim.plugin.security +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.deltacv.eocvsim.plugin.security import com.github.serivesmejia.eocvsim.util.extension.hashString -import io.github.deltacv.common.util.loggerForThis -import com.moandjiezana.toml.Toml -import io.github.deltacv.eocvsim.plugin.loader.InvalidPluginException +import org.deltacv.common.util.loggerForThis +import org.deltacv.common.util.Toml +import org.deltacv.eocvsim.plugin.loader.InvalidPluginException import java.io.File import java.security.PublicKey import java.security.Signature @@ -51,8 +51,10 @@ object PluginSignatureVerifier { return emptyResult } - val signatureToml = zip.getInputStream(signatureEntry).bufferedReader() - val signature = Toml().read(signatureToml) + // Read the signature TOML from the entry as an InputStream so it matches + // the Toml.read(InputStream) overload (avoid BufferedReader which is not supported) + // force the Toml type so Kotlin sees the helper API + val signature: Toml = zip.getInputStream(signatureEntry).use { Toml().read(it) } val authorityName = signature.getString("authority") if (authorityName == null) { @@ -77,7 +79,7 @@ object PluginSignatureVerifier { return emptyResult } - val signatureData = signature.getTable("signatures") ?: run { + val signatureData: Toml = signature.getTable("signatures") ?: run { logger.warn("signature.toml does not contain a signatures table") return emptyResult } @@ -88,13 +90,15 @@ object PluginSignatureVerifier { val signatures = mutableMapOf() // Verify each signature - for ((path, sign) in signatureData.toMap()) { - if(sign !is String) { + // Convert table to a typed map so Kotlin can destructure entries reliably + val signatureMap = signatureData.toMap() as Map + for ((path, signAny) in signatureMap) { + if (signAny !is String) { logger.warn("Signature for class $path is not a string") return emptyResult } - signatures[path] = sign + signatures[path] = signAny } // Now, verify each class in the JAR file diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSigningTool.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/security/PluginSigningTool.kt similarity index 96% rename from Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSigningTool.kt rename to Common/src/main/java/org/deltacv/eocvsim/plugin/security/PluginSigningTool.kt index 4db0582a..b2293b11 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/plugin/security/PluginSigningTool.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/security/PluginSigningTool.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.security +package org.deltacv.eocvsim.plugin.security import com.github.serivesmejia.eocvsim.util.extension.hashString import picocli.CommandLine diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/sandbox/nio/SandboxFileSystem.kt b/Common/src/main/java/org/deltacv/eocvsim/sandbox/nio/SandboxFileSystem.kt similarity index 97% rename from Common/src/main/java/io/github/deltacv/eocvsim/sandbox/nio/SandboxFileSystem.kt rename to Common/src/main/java/org/deltacv/eocvsim/sandbox/nio/SandboxFileSystem.kt index 9622a8ef..4d7b8223 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/sandbox/nio/SandboxFileSystem.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/sandbox/nio/SandboxFileSystem.kt @@ -3,9 +3,9 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.sandbox.nio +package org.deltacv.eocvsim.sandbox.nio -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import java.nio.file.* import java.nio.file.attribute.FileAttribute diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/DynamicCodeRestrictions.kt b/Common/src/main/java/org/deltacv/eocvsim/sandbox/restrictions/DynamicCodeRestrictions.kt similarity index 93% rename from Common/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/DynamicCodeRestrictions.kt rename to Common/src/main/java/org/deltacv/eocvsim/sandbox/restrictions/DynamicCodeRestrictions.kt index 846f723a..c245b7fc 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/DynamicCodeRestrictions.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/sandbox/restrictions/DynamicCodeRestrictions.kt @@ -1,9 +1,9 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - -package io.github.deltacv.eocvsim.sandbox.restrictions +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.deltacv.eocvsim.sandbox.restrictions /** * === Dynamic Code Sandbox Restrictions === @@ -37,10 +37,10 @@ package io.github.deltacv.eocvsim.sandbox.restrictions * Denies access to entire namespaces considered dangerous. * * - If a class name contains a blacklisted package name, loading is denied. - * - HOWEVER: if the class matched the whitelist, this blacklist is SKIPPED. + * - This list is checked AFTER the whitelist, meaning it OVERRIDES the whitelist. * * NOTE: - * This means whitelist entries OVERRIDE this blacklist. + * This is used to block dangerous sub-packages within a whitelisted parent package. * * AUTHORITY: * - Higher authority than the whitelist @@ -137,11 +137,11 @@ val dynamicCodePackageWhitelist = setOf( "android", // API - "io.github.deltacv.eocvsim.plugin", - "io.github.deltacv.eocvsim.sandbox", + "org.deltacv.eocvsim.plugin", + "org.deltacv.eocvsim.sandbox", // Third-party libs explicitly allowed - "com.moandjiezana.toml", + "org.deltacv.common.util", "net.lingala.zip4j", "com.google.jimfs", "org.slf4j", @@ -251,7 +251,7 @@ val dynamicCodePackageBlacklist = setOf( val dynamicCodePackageAlwaysBlacklist = setOf( // Embedded PaperVision (avoid duplicate / unsafe classloading) - "io.github.deltacv.papervision", + "org.deltacv.papervision", // Logging system isolation "org.apache.logging.log4j" @@ -314,4 +314,3 @@ val dynamicCodeMethodBlacklist = setOf( "java.io.File#isHidden", "java.io.File#lastModified" ) - diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualField.kt b/Common/src/main/java/org/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualField.kt similarity index 88% rename from Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualField.kt rename to Common/src/main/java/org/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualField.kt index 3b3256ad..2c687eb5 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualField.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualField.kt @@ -3,10 +3,10 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.virtualreflect.jvm +package org.deltacv.eocvsim.virtualreflect.jvm -import io.github.deltacv.eocvsim.virtualreflect.VirtualField -import io.github.deltacv.eocvsim.virtualreflect.Visibility +import org.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.Visibility import java.lang.reflect.Field import java.lang.reflect.Modifier diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflectContext.kt b/Common/src/main/java/org/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflectContext.kt similarity index 86% rename from Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflectContext.kt rename to Common/src/main/java/org/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflectContext.kt index 22c6615f..38c1dba2 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflectContext.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflectContext.kt @@ -3,10 +3,10 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.virtualreflect.jvm +package org.deltacv.eocvsim.virtualreflect.jvm -import io.github.deltacv.eocvsim.virtualreflect.VirtualReflectContext -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualReflectContext +import org.deltacv.eocvsim.virtualreflect.VirtualField import java.lang.reflect.Field class JvmVirtualReflectContext( diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflection.kt b/Common/src/main/java/org/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflection.kt similarity index 79% rename from Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflection.kt rename to Common/src/main/java/org/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflection.kt index d150ee6b..b2e4303e 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflection.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/virtualreflect/jvm/JvmVirtualReflection.kt @@ -3,10 +3,10 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.virtualreflect.jvm +package org.deltacv.eocvsim.virtualreflect.jvm -import io.github.deltacv.eocvsim.virtualreflect.VirtualReflectContext -import io.github.deltacv.eocvsim.virtualreflect.VirtualReflection +import org.deltacv.eocvsim.virtualreflect.VirtualReflectContext +import org.deltacv.eocvsim.virtualreflect.VirtualReflection import java.lang.ref.WeakReference import java.util.* diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/Label.java b/Common/src/main/java/org/deltacv/eocvsim/virtualreflect/jvm/Label.java similarity index 89% rename from Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/Label.java rename to Common/src/main/java/org/deltacv/eocvsim/virtualreflect/jvm/Label.java index 2b9ceba3..16674f78 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/jvm/Label.java +++ b/Common/src/main/java/org/deltacv/eocvsim/virtualreflect/jvm/Label.java @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.virtualreflect.jvm; +package org.deltacv.eocvsim.virtualreflect.jvm; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualField.kt b/Common/src/main/java/org/deltacv/eocvsim/virtualreflect/virtualreflect/VirtualField.kt similarity index 90% rename from Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualField.kt rename to Common/src/main/java/org/deltacv/eocvsim/virtualreflect/virtualreflect/VirtualField.kt index cdc8661b..3da92de3 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualField.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/virtualreflect/virtualreflect/VirtualField.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.virtualreflect +package org.deltacv.eocvsim.virtualreflect /** * Represents a field of a class, but is not necessarily backed by an actual Java Field. diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflectContext.kt b/Common/src/main/java/org/deltacv/eocvsim/virtualreflect/virtualreflect/VirtualReflectContext.kt similarity index 88% rename from Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflectContext.kt rename to Common/src/main/java/org/deltacv/eocvsim/virtualreflect/virtualreflect/VirtualReflectContext.kt index 566a80a5..77aa4c92 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflectContext.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/virtualreflect/virtualreflect/VirtualReflectContext.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.virtualreflect +package org.deltacv.eocvsim.virtualreflect /** * Context for virtual reflection, which makes for a multi-platform way to reflect on classes, fields, and methods. @@ -32,7 +32,7 @@ interface VirtualReflectContext { /** * Gets a field by its label, or null if it doesn't exist - * @see io.github.deltacv.eocvsim.virtualreflect.jvm.Label + * @see org.deltacv.eocvsim.virtualreflect.jvm.Label */ fun getLabeledField(label: String): VirtualField? diff --git a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflection.kt b/Common/src/main/java/org/deltacv/eocvsim/virtualreflect/virtualreflect/VirtualReflection.kt similarity index 87% rename from Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflection.kt rename to Common/src/main/java/org/deltacv/eocvsim/virtualreflect/virtualreflect/VirtualReflection.kt index 19df43d2..ee86dcb6 100644 --- a/Common/src/main/java/io/github/deltacv/eocvsim/virtualreflect/VirtualReflection.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/virtualreflect/virtualreflect/VirtualReflection.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.virtualreflect +package org.deltacv.eocvsim.virtualreflect /** * Interface for virtual reflection, which allows to get a [VirtualReflectContext] from a class or an instance diff --git a/EOCV-Sim/build.gradle b/EOCV-Sim/build.gradle index 27f696e5..6cad12e5 100644 --- a/EOCV-Sim/build.gradle +++ b/EOCV-Sim/build.gradle @@ -160,7 +160,7 @@ dependencies { testImplementation "io.kotest:kotest-runner-junit5:$kotest_version" testImplementation "io.kotest:kotest-assertions-core:$kotest_version" - implementation "com.moandjiezana.toml:toml4j:$toml4j_version" + implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-toml:$jackson_version" implementation 'org.ow2.asm:asm:9.7' implementation 'org.jboss.shrinkwrap.resolver:shrinkwrap-resolver-depchain:3.3.3' 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 f5e725d5..141651ff 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 @@ -24,10 +24,9 @@ import com.github.serivesmejia.eocvsim.util.io.EOCVSimFolder import com.github.serivesmejia.eocvsim.util.orchestration.Orchestrator import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager 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.common.util.loggerFor -import io.github.deltacv.eocvsim.plugin.loader.PluginManager +import org.deltacv.common.pipeline.PipelineStatisticsCalculator +import org.deltacv.common.util.ParsedVersion +import org.deltacv.common.util.loggerFor import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -95,7 +94,7 @@ class EOCVSim : KoinComponent { val configManager: ConfigManager by inject() val inputSourceManager: InputSourceManager by inject() - val pluginManager: PluginManager by inject() + val pluginManager: org.deltacv.eocvsim.plugin.loader.PluginManager by inject() val recordingManager: RecordingManager by inject() val dialogFactory: DialogFactory by inject() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt index 53bc1fd0..776a72d1 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/Module.kt @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim import com.github.serivesmejia.eocvsim.config.ConfigManager @@ -21,12 +21,12 @@ import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.util.orchestration.Orchestrable import com.github.serivesmejia.eocvsim.util.orchestration.Orchestrator import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager -import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator -import io.github.deltacv.eocvsim.plugin.loader.PluginManager +import org.deltacv.common.pipeline.PipelineStatisticsCalculator import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.channels.Channel +import org.deltacv.eocvsim.plugin.loader.PluginManager import org.koin.core.definition.KoinDefinition import org.koin.core.qualifier.named import org.koin.dsl.bind diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt index 871a1af5..4179b09c 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/config/ConfigManager.kt @@ -6,7 +6,7 @@ package com.github.serivesmejia.eocvsim.config import com.github.serivesmejia.eocvsim.util.orchestration.PhaseOrchestrableBase -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import org.koin.core.component.KoinComponent class ConfigManager : PhaseOrchestrableBase(), KoinComponent { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt index 54d75257..7d7a2847 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt @@ -14,7 +14,7 @@ import com.github.serivesmejia.eocvsim.input.SourceType import com.github.serivesmejia.eocvsim.plugin.output.PluginOutputHandler import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.util.exception.handling.CrashReport -import io.github.deltacv.eocvsim.plugin.loader.PluginManager +import org.deltacv.eocvsim.plugin.loader.PluginManager import kotlinx.coroutines.CoroutineScope import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -28,7 +28,7 @@ import javax.swing.filechooser.FileNameExtensionFilter class DialogFactory : KoinComponent { private val visualizer: Visualizer by inject() - private val pluginManager: PluginManager by inject() + private val pluginManager: org.deltacv.eocvsim.plugin.loader.PluginManager by inject() private val configManager: ConfigManager by inject() private val scope: CoroutineScope by inject() private val outputHandler: PluginOutputHandler by inject() @@ -252,4 +252,4 @@ class DialogFactory : KoinComponent { } } } - + diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Icons.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Icons.kt index 482645b1..c7b7555b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Icons.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Icons.kt @@ -6,8 +6,8 @@ package com.github.serivesmejia.eocvsim.gui import com.github.serivesmejia.eocvsim.gui.util.GuiUtil -import io.github.deltacv.common.util.loggerForThis -import io.github.deltacv.vision.external.gui.util.ImgUtil +import org.deltacv.common.util.loggerForThis +import org.deltacv.vision.external.gui.util.ImgUtil import java.awt.image.BufferedImage import java.util.NoSuchElementException import org.koin.core.component.KoinComponent diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt index e881f4d2..d50814c1 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt @@ -21,7 +21,6 @@ import com.github.serivesmejia.eocvsim.gui.component.visualizer.opmode.SidebarOp import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.PipelineSelectorPanel import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SidebarPipelineTabPanel import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SourceSelectorPanel -import com.github.serivesmejia.eocvsim.gui.dialog.PluginOutput import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.github.serivesmejia.eocvsim.output.RecordingManager import com.github.serivesmejia.eocvsim.pipeline.PipelineManager @@ -33,9 +32,9 @@ import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager import com.github.serivesmejia.eocvsim.workspace.util.VSCodeLauncher import com.github.serivesmejia.eocvsim.workspace.util.template.GradleWorkspaceTemplate import com.qualcomm.robotcore.eventloop.opmode.OpMode -import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator -import io.github.deltacv.common.util.loggerForThis -import io.github.deltacv.vision.external.gui.SwingOpenCvViewport +import org.deltacv.common.pipeline.PipelineStatisticsCalculator +import org.deltacv.common.util.loggerForThis +import org.deltacv.vision.external.gui.SwingOpenCvViewport import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/ColorPicker.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/ColorPicker.kt index 408d5dd2..544874a6 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/ColorPicker.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/ColorPicker.kt @@ -8,7 +8,7 @@ package com.github.serivesmejia.eocvsim.gui.component.tuner import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler -import io.github.deltacv.vision.external.gui.SwingOpenCvViewport +import org.deltacv.vision.external.gui.SwingOpenCvViewport import org.opencv.core.Scalar import java.awt.Cursor import java.awt.Point diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelConfig.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelConfig.kt index eea8b933..bac0156c 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelConfig.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/tuner/TunableFieldPanelConfig.kt @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.tuner import com.github.serivesmejia.eocvsim.config.ConfigManager @@ -12,7 +12,7 @@ import com.github.serivesmejia.eocvsim.gui.component.input.SizeFields import com.github.serivesmejia.eocvsim.tuner.TunableField import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.util.event.EventHandler -import io.github.deltacv.eocvsim.plugin.loader.PluginManager +import org.deltacv.eocvsim.plugin.loader.PluginManager import kotlinx.coroutines.* import kotlinx.coroutines.swing.Swing import org.opencv.core.Size 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 125d8f52..464f8249 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 @@ -9,7 +9,7 @@ import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary import com.github.serivesmejia.eocvsim.gui.component.PopupX import com.github.serivesmejia.eocvsim.tuner.TunableNumber -import io.github.deltacv.vision.external.util.extension.cvtColor +import org.deltacv.vision.external.util.extension.cvtColor import com.github.serivesmejia.eocvsim.util.extension.clipUpperZero import java.awt.FlowLayout import java.awt.GridLayout diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/InputSourceDropTarget.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/InputSourceDropTarget.kt index a1392e62..55678040 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/InputSourceDropTarget.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/InputSourceDropTarget.kt @@ -8,7 +8,7 @@ package com.github.serivesmejia.eocvsim.gui.component.visualizer import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.input.SourceType -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import org.koin.core.component.KoinComponent import org.koin.core.component.inject import java.awt.datatransfer.DataFlavor diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/SidebarPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/SidebarPanel.kt index 871d3476..19e06ffd 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/SidebarPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/SidebarPanel.kt @@ -7,7 +7,7 @@ package com.github.serivesmejia.eocvsim.gui.component.visualizer import com.github.serivesmejia.eocvsim.util.event.EventHandler -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import java.awt.Font import java.awt.LayoutManager import javax.swing.JPanel 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 4cdbf59a..29249d80 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 @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.component.visualizer import com.github.serivesmejia.eocvsim.LifecycleSignal @@ -27,7 +27,7 @@ import javax.swing.JMenu import javax.swing.JMenuBar import javax.swing.JMenuItem -import io.github.deltacv.eocvsim.plugin.loader.PluginManager +import org.deltacv.eocvsim.plugin.loader.PluginManager import com.github.serivesmejia.eocvsim.workspace.WorkspaceManager import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.util.event.EventHandler @@ -41,7 +41,7 @@ class TopMenuBar : JMenuBar(), KoinComponent { val visualizer: Visualizer by inject() val dialogFactory: DialogFactory by inject() - val pluginManager: PluginManager by inject() + val pluginManager: org.deltacv.eocvsim.plugin.loader.PluginManager by inject() val outputHandler: PluginOutputHandler by inject() val workspaceManager: WorkspaceManager by inject() val pipelineManager: PipelineManager by inject() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeControlsPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeControlsPanel.kt index 39378614..df48e8ba 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeControlsPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeControlsPanel.kt @@ -8,8 +8,8 @@ package com.github.serivesmejia.eocvsim.gui.component.visualizer.opmode import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.qualcomm.robotcore.eventloop.opmode.OpMode -import io.github.deltacv.vision.internal.opmode.OpModeNotification -import io.github.deltacv.vision.internal.opmode.OpModeState +import org.deltacv.vision.internal.opmode.OpModeNotification +import org.deltacv.vision.internal.opmode.OpModeState import java.awt.BorderLayout import javax.swing.JPanel import javax.swing.JButton diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeSelectorPanel.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeSelectorPanel.kt index dbdffb07..5b85e77b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeSelectorPanel.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/component/visualizer/opmode/OpModeSelectorPanel.kt @@ -12,10 +12,10 @@ import com.github.serivesmejia.eocvsim.gui.util.Corner import com.github.serivesmejia.eocvsim.gui.util.icon.PipelineListIconRenderer import com.github.serivesmejia.eocvsim.pipeline.PipelineData import com.github.serivesmejia.eocvsim.util.ReflectUtil -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import com.qualcomm.robotcore.eventloop.opmode.* import com.qualcomm.robotcore.util.Range -import io.github.deltacv.vision.internal.opmode.OpModeState +import org.deltacv.vision.internal.opmode.OpModeState import java.awt.GridBagConstraints import java.awt.event.MouseEvent import javax.swing.* diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.kt index 6f834036..3f129111 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/About.kt @@ -10,7 +10,7 @@ import com.github.serivesmejia.eocvsim.gui.EOCVSimIconLibrary import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.gui.util.GuiUtil import com.github.serivesmejia.eocvsim.util.StrUtil -import io.github.deltacv.vision.external.gui.component.ImageX +import org.deltacv.vision.external.gui.component.ImageX import org.koin.core.component.KoinComponent import org.koin.core.component.inject import java.awt.* 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 248cae4e..94e8814f 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 @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog import com.github.serivesmejia.eocvsim.LifecycleSignal @@ -12,10 +12,10 @@ import com.github.serivesmejia.eocvsim.gui.dialog.component.OutputPanel import com.github.serivesmejia.eocvsim.plugin.output.PluginDialogSignal import com.github.serivesmejia.eocvsim.plugin.output.PluginOutputHandler import com.github.serivesmejia.eocvsim.plugin.output.VisualPluginOutputHandler -import io.github.deltacv.eocvsim.plugin.loader.FilePluginLoaderImpl -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 org.deltacv.eocvsim.plugin.loader.FilePluginLoaderImpl +import org.deltacv.eocvsim.plugin.loader.PluginManager +import org.deltacv.eocvsim.plugin.loader.PluginSource +import org.deltacv.eocvsim.plugin.repository.PluginRepositoryManager import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.Channel import org.koin.core.component.KoinComponent @@ -27,7 +27,7 @@ import javax.swing.* class PluginOutput( val outputHandler: PluginOutputHandler, - val pluginManager: PluginManager, + val pluginManager: org.deltacv.eocvsim.plugin.loader.PluginManager, val configManager: ConfigManager, val scope: CoroutineScope ) : Appendable, KoinComponent { @@ -352,7 +352,7 @@ class PluginOutput( ) if(dialogResult == JOptionPane.YES_OPTION) { - configManager.config.flags["startFresh"] = true + configManager.config.flags["startFreshPlugins"] = true PluginRepositoryManager.REPOSITORY_FILE.delete() PluginRepositoryManager.CACHE_FILE.delete() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt index 7c4c7b8c..d3fbd06d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt @@ -11,7 +11,7 @@ import com.github.serivesmejia.eocvsim.gui.component.input.FileSelector import com.github.serivesmejia.eocvsim.gui.component.input.SizeFields import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.github.serivesmejia.eocvsim.input.source.ImageSource -import io.github.deltacv.vision.external.util.CvUtil +import org.deltacv.vision.external.util.CvUtil import com.github.serivesmejia.eocvsim.util.FileFilters import com.github.serivesmejia.eocvsim.util.StrUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt index 6346aea2..b3aed7dd 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt @@ -13,7 +13,7 @@ import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.github.serivesmejia.eocvsim.input.source.VideoSource import com.github.serivesmejia.eocvsim.util.FileFilters import com.github.serivesmejia.eocvsim.util.event.EventHandler -import io.github.deltacv.vision.external.util.CvUtil +import org.deltacv.vision.external.util.CvUtil import org.opencv.core.Size import java.awt.BorderLayout import java.awt.FlowLayout diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/GuiUtil.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/GuiUtil.kt index 3a3b89b6..0b182038 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/GuiUtil.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/GuiUtil.kt @@ -8,8 +8,8 @@ package com.github.serivesmejia.eocvsim.gui.util import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.dialog.FileAlreadyExists import com.github.serivesmejia.eocvsim.util.SysUtil -import io.github.deltacv.common.util.loggerForThis -import io.github.deltacv.vision.external.util.CvUtil +import org.deltacv.common.util.loggerForThis +import org.deltacv.vision.external.util.CvUtil import org.opencv.core.Mat import org.slf4j.LoggerFactory import java.awt.Color diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/ThreadedMatPoster.java b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/ThreadedMatPoster.java index 5aeae12e..5f83f825 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/ThreadedMatPoster.java +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/util/ThreadedMatPoster.java @@ -6,7 +6,7 @@ package com.github.serivesmejia.eocvsim.gui.util; import com.github.serivesmejia.eocvsim.util.fps.FpsCounter; -import io.github.deltacv.common.image.MatPoster; +import org.deltacv.common.image.MatPoster; import org.firstinspires.ftc.robotcore.internal.collections.EvictingBlockingQueue; import org.opencv.core.Mat; import org.openftc.easyopencv.MatRecycler; diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt index ca8eee35..a80d12bc 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceInitializer.kt @@ -6,7 +6,7 @@ package com.github.serivesmejia.eocvsim.input import com.github.serivesmejia.eocvsim.util.event.ParamEventHandler -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import kotlinx.coroutines.* import org.koin.core.component.KoinComponent import org.koin.core.component.inject diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt index b0148605..79e4c292 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/InputSourceManager.kt @@ -12,7 +12,7 @@ import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.util.orchestration.PhaseOrchestrableBase -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.koin.core.qualifier.named diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt index e46abf4b..2ae9550f 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/CameraSource.kt @@ -10,7 +10,7 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.github.serivesmejia.eocvsim.input.InputSource import com.github.serivesmejia.eocvsim.input.InputSourceInitializer import com.github.serivesmejia.eocvsim.config.ConfigManager -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.opencv.core.Mat diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt index eb1fff1e..2f8621fe 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/input/source/HttpSource.kt @@ -10,7 +10,7 @@ import com.fasterxml.jackson.annotation.JsonProperty import com.github.serivesmejia.eocvsim.input.InputSource import com.github.serivesmejia.eocvsim.input.InputSourceInitializer import com.github.serivesmejia.eocvsim.config.ConfigManager -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.opencv.core.Mat diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/RecordingManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/RecordingManager.kt index 87f56ac8..bb49187f 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/RecordingManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/RecordingManager.kt @@ -12,7 +12,7 @@ import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.util.FileFilters import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.koin.core.qualifier.named diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/VideoRecordingSession.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/VideoRecordingSession.kt index fa7b7103..bcbc1731 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/VideoRecordingSession.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/output/VideoRecordingSession.kt @@ -7,10 +7,10 @@ package com.github.serivesmejia.eocvsim.output import com.github.serivesmejia.eocvsim.gui.util.ThreadedMatPoster import com.github.serivesmejia.eocvsim.util.StrUtil -import io.github.deltacv.vision.external.util.extension.aspectRatio -import io.github.deltacv.vision.external.util.extension.clipTo +import org.deltacv.vision.external.util.extension.aspectRatio +import org.deltacv.vision.external.util.extension.clipTo import com.github.serivesmejia.eocvsim.util.fps.FpsCounter -import io.github.deltacv.common.image.MatPoster +import org.deltacv.common.image.MatPoster import org.opencv.core.* import org.opencv.imgproc.Imgproc import org.opencv.videoio.VideoWriter diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt index 84f45991..8e6d7707 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/PipelineManager.kt @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.pipeline import com.github.serivesmejia.eocvsim.EOCVSim @@ -27,12 +27,12 @@ import com.github.serivesmejia.eocvsim.util.orchestration.initDependency import com.github.serivesmejia.eocvsim.util.orchestration.runDependency import com.github.serivesmejia.eocvsim.util.orchestration.PhaseOrchestrableBase import com.github.serivesmejia.eocvsim.util.fps.FpsCounter -import io.github.deltacv.common.image.MatPoster -import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator -import io.github.deltacv.common.util.loggerForThis -import io.github.deltacv.eocvsim.virtualreflect.VirtualField -import io.github.deltacv.eocvsim.virtualreflect.VirtualReflection -import io.github.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflection +import org.deltacv.common.image.MatPoster +import org.deltacv.common.pipeline.PipelineStatisticsCalculator +import org.deltacv.common.util.loggerForThis +import org.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualReflection +import org.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflection import kotlinx.coroutines.* import org.firstinspires.ftc.robotcore.external.Telemetry import org.firstinspires.ftc.robotcore.internal.opmode.EOCVSimTelemetryImpl diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/CompiledPipelineManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/CompiledPipelineManager.kt index 9962cd19..5cc96957 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/CompiledPipelineManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/CompiledPipelineManager.kt @@ -13,7 +13,7 @@ import com.github.serivesmejia.eocvsim.pipeline.PipelineSource import com.github.serivesmejia.eocvsim.util.StrUtil import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import com.github.serivesmejia.eocvsim.workspace.config.WorkspaceConfigLoader import com.github.serivesmejia.eocvsim.workspace.util.template.DefaultWorkspaceTemplate import com.qualcomm.robotcore.util.ElapsedTime diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineClassLoader.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineClassLoader.kt index 8f5dc01f..297857b2 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineClassLoader.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineClassLoader.kt @@ -8,8 +8,8 @@ package com.github.serivesmejia.eocvsim.pipeline.compiled import com.github.serivesmejia.eocvsim.util.ClasspathScan import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.extension.removeFromEnd -import io.github.deltacv.eocvsim.sandbox.restrictions.MethodCallByteCodeChecker -import io.github.deltacv.eocvsim.sandbox.restrictions.dynamicCodeMethodBlacklist +import org.deltacv.eocvsim.sandbox.restrictions.MethodCallByteCodeChecker +import org.deltacv.eocvsim.sandbox.restrictions.dynamicCodeMethodBlacklist import org.openftc.easyopencv.OpenCvPipeline import java.io.ByteArrayOutputStream import java.io.File diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineCompiler.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineCompiler.kt index bd390d69..f80315e4 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineCompiler.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineCompiler.kt @@ -8,7 +8,7 @@ package com.github.serivesmejia.eocvsim.pipeline.compiled import com.github.serivesmejia.eocvsim.util.* import com.github.serivesmejia.eocvsim.util.compiler.JarPacker import com.github.serivesmejia.eocvsim.util.compiler.compiler -import io.github.deltacv.common.util.loggerFor +import org.deltacv.common.util.loggerFor import java.io.File import java.io.PrintWriter import java.nio.charset.Charset diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineStandardFileManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineStandardFileManager.kt index c8a80405..7bdf6be0 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineStandardFileManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/compiled/PipelineStandardFileManager.kt @@ -7,7 +7,7 @@ package com.github.serivesmejia.eocvsim.pipeline.compiled import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.compiler.DelegatingStandardFileManager -import io.github.deltacv.common.util.loggerFor +import org.deltacv.common.util.loggerFor import java.io.File import java.util.* import javax.tools.StandardJavaFileManager diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/processor/ProcessorInstantiator.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/processor/ProcessorInstantiator.kt index 9046eee7..649e894d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/processor/ProcessorInstantiator.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/instantiator/processor/ProcessorInstantiator.kt @@ -7,7 +7,7 @@ package com.github.serivesmejia.eocvsim.pipeline.instantiator.processor import com.github.serivesmejia.eocvsim.pipeline.instantiator.PipelineInstantiator import com.github.serivesmejia.eocvsim.util.ReflectUtil -import io.github.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflection +import org.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflection import org.firstinspires.ftc.robotcore.external.Telemetry import org.firstinspires.ftc.vision.VisionProcessor import org.openftc.easyopencv.OpenCvPipeline diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineExceptionTracker.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineExceptionTracker.kt index 1de1d21c..6d620529 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineExceptionTracker.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineExceptionTracker.kt @@ -9,7 +9,7 @@ import com.github.serivesmejia.eocvsim.pipeline.PipelineData import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.util.StrUtil -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis class PipelineExceptionTracker(private val pipelineManager: PipelineManager) { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineSnapshot.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineSnapshot.kt index 4316e498..bbc4b74a 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineSnapshot.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/pipeline/util/PipelineSnapshot.kt @@ -5,11 +5,11 @@ package com.github.serivesmejia.eocvsim.pipeline.util -import io.github.deltacv.common.util.loggerForThis -import io.github.deltacv.eocvsim.virtualreflect.VirtualField -import io.github.deltacv.eocvsim.virtualreflect.VirtualReflectContext -import io.github.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflectContext -import io.github.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflection +import org.deltacv.common.util.loggerForThis +import org.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualReflectContext +import org.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflectContext +import org.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflection import org.openftc.easyopencv.OpenCvPipeline import java.util.* diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/ConfigApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/ConfigApiImpl.kt index d1f142b1..887859de 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/ConfigApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/ConfigApiImpl.kt @@ -6,8 +6,8 @@ package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.config.ConfigManager -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.ConfigApi +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.api.ConfigApi class ConfigApiImpl(owner: EOCVSimPlugin, val internalConfigManager: ConfigManager) : ConfigApi(owner) { override fun putFlag(flag: String) = apiImpl { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/DialogFactoryApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/DialogFactoryApiImpl.kt index 82cd6895..9bf6e0e0 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/DialogFactoryApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/DialogFactoryApiImpl.kt @@ -9,11 +9,11 @@ import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.input.SourceType import com.github.serivesmejia.eocvsim.util.event.EventHandler -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.DialogFactoryApi -import io.github.deltacv.eocvsim.plugin.api.HookApi -import io.github.deltacv.eocvsim.plugin.api.InputSourceApi -import io.github.deltacv.eocvsim.plugin.api.JFileChooserApi +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.api.DialogFactoryApi +import org.deltacv.eocvsim.plugin.api.HookApi +import org.deltacv.eocvsim.plugin.api.InputSourceApi +import org.deltacv.eocvsim.plugin.api.JFileChooserApi import org.koin.core.component.KoinComponent import org.koin.core.component.inject import java.io.File diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt index 43ec60cc..53e9b691 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EOCVSimApiImpl.kt @@ -6,14 +6,14 @@ package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.EOCVSim -import io.github.deltacv.common.util.loggerForThis -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.ConfigApi -import io.github.deltacv.eocvsim.plugin.api.EOCVSimApi -import io.github.deltacv.eocvsim.plugin.api.InputSourceManagerApi -import io.github.deltacv.eocvsim.plugin.api.PipelineManagerApi -import io.github.deltacv.eocvsim.plugin.api.VariableTunerApi -import io.github.deltacv.eocvsim.plugin.api.VisualizerApi +import org.deltacv.common.util.loggerForThis +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.api.ConfigApi +import org.deltacv.eocvsim.plugin.api.EOCVSimApi +import org.deltacv.eocvsim.plugin.api.InputSourceManagerApi +import org.deltacv.eocvsim.plugin.api.PipelineManagerApi +import org.deltacv.eocvsim.plugin.api.VariableTunerApi +import org.deltacv.eocvsim.plugin.api.VisualizerApi import org.koin.core.component.KoinComponent import org.koin.core.component.inject diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EventHandlerHookApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EventHandlerHookApiImpl.kt index 32cb1ecf..3d85f119 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EventHandlerHookApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/EventHandlerHookApiImpl.kt @@ -1,16 +1,14 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.util.event.EventHandler -import com.github.serivesmejia.eocvsim.util.event.EventListener import com.github.serivesmejia.eocvsim.util.event.EventListenerId -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.HookApi -import java.lang.ref.WeakReference +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.api.HookApi class EventHandlerHookApiImpl(owner: EOCVSimPlugin, val eventHandler: EventHandler) : HookApi(owner) { private var listeners = mutableListOf() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt index a103df89..ffda12a8 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/InputSourceApisImpl.kt @@ -10,9 +10,9 @@ import com.github.serivesmejia.eocvsim.input.source.CameraSource import com.github.serivesmejia.eocvsim.input.source.HttpSource import com.github.serivesmejia.eocvsim.input.source.ImageSource import com.github.serivesmejia.eocvsim.input.source.VideoSource -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.InputSourceApi -import io.github.deltacv.eocvsim.plugin.api.InputSourceManagerApi +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.api.InputSourceApi +import org.deltacv.eocvsim.plugin.api.InputSourceManagerApi import org.opencv.core.Size class InputSourceApiImpl(owner: EOCVSimPlugin, val internalInputSource: com.github.serivesmejia.eocvsim.input.InputSource) : InputSourceApi(owner) { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/PipelineManagerApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/PipelineManagerApiImpl.kt index 93b7f198..5c98cbf1 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/PipelineManagerApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/PipelineManagerApiImpl.kt @@ -7,10 +7,10 @@ package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.pipeline.PipelineManager import com.github.serivesmejia.eocvsim.pipeline.instantiator.PipelineInstantiator -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.PipelineInstantiatorApi -import io.github.deltacv.eocvsim.plugin.api.PipelineManagerApi -import io.github.deltacv.eocvsim.plugin.api.PipelineManagerApi.PipelineSource +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.api.PipelineInstantiatorApi +import org.deltacv.eocvsim.plugin.api.PipelineManagerApi +import org.deltacv.eocvsim.plugin.api.PipelineManagerApi.PipelineSource import org.firstinspires.ftc.robotcore.external.Telemetry import org.openftc.easyopencv.OpenCvPipeline diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SimpleHookApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SimpleHookApiImpl.kt index 93ac1e2c..83190cbe 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SimpleHookApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SimpleHookApiImpl.kt @@ -5,8 +5,8 @@ package com.github.serivesmejia.eocvsim.plugin.api.impl -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.HookApi +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.api.HookApi class SimpleHookApiImpl(owner: EOCVSimPlugin) : HookApi(owner) { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SwingApisImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SwingApisImpl.kt index 952b48d0..ed88c0a6 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SwingApisImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/SwingApisImpl.kt @@ -6,10 +6,10 @@ package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.gui.DialogFactory -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.JFileChooserApi -import io.github.deltacv.eocvsim.plugin.api.JMenuApi -import io.github.deltacv.eocvsim.plugin.api.JMenuItemApi +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.api.JFileChooserApi +import org.deltacv.eocvsim.plugin.api.JMenuApi +import org.deltacv.eocvsim.plugin.api.JMenuItemApi import java.io.File import javax.swing.JFileChooser import javax.swing.JMenu diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VariableTunerApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VariableTunerApiImpl.kt index b44082fb..a1add672 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VariableTunerApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VariableTunerApiImpl.kt @@ -1,17 +1,16 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.tuner.TunableField -import com.github.serivesmejia.eocvsim.tuner.TunableNumber import com.github.serivesmejia.eocvsim.tuner.TunerManager -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.TunableFieldApi -import io.github.deltacv.eocvsim.plugin.api.VariableTunerApi -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.api.TunableFieldApi +import org.deltacv.eocvsim.plugin.api.VariableTunerApi +import org.deltacv.eocvsim.virtualreflect.VirtualField class TunableFieldApiImpl(owner: EOCVSimPlugin, val internalTunableField: TunableField<*>) : TunableFieldApi(owner) { override val field: VirtualField by liveApiField { internalTunableField.reflectionField } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerApiImpl.kt index f43b9d10..e959f4a4 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerApiImpl.kt @@ -8,9 +8,9 @@ package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.gui.Visualizer import com.github.serivesmejia.eocvsim.gui.component.visualizer.SidebarPanel import com.github.serivesmejia.eocvsim.gui.component.visualizer.TopMenuBar -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.* -import io.github.deltacv.vision.external.gui.SwingOpenCvViewport +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.api.* +import org.deltacv.vision.external.gui.SwingOpenCvViewport class VisualizerApiImpl(owner: EOCVSimPlugin, val internalVisualizer: Visualizer) : VisualizerApi(owner) { override val frame by liveNullableApiField { internalVisualizer.frame } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerComponentsApiImpl.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerComponentsApiImpl.kt index 4ba28306..be975275 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerComponentsApiImpl.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/api/impl/VisualizerComponentsApiImpl.kt @@ -8,11 +8,11 @@ package com.github.serivesmejia.eocvsim.plugin.api.impl import com.github.serivesmejia.eocvsim.gui.component.visualizer.TelemetryPanel import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.PipelineSelectorPanel import com.github.serivesmejia.eocvsim.gui.component.visualizer.pipeline.SourceSelectorPanel -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.PipelineSelectorPanelApi -import io.github.deltacv.eocvsim.plugin.api.SourceSelectorPanelApi -import io.github.deltacv.eocvsim.plugin.api.TelemetryPanelApi -import io.github.deltacv.eocvsim.plugin.api.VisualizerComponentsFactoryApi +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.api.PipelineSelectorPanelApi +import org.deltacv.eocvsim.plugin.api.SourceSelectorPanelApi +import org.deltacv.eocvsim.plugin.api.TelemetryPanelApi +import org.deltacv.eocvsim.plugin.api.VisualizerComponentsFactoryApi class VisualizerComponentsFactoryApiImpl(owner: EOCVSimPlugin) : VisualizerComponentsFactoryApi(owner) { override fun createPipelineSelectorPanel() = apiImpl { PipelineSelectorPanelApiImpl(owner, PipelineSelectorPanel()) } diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/VisualPluginOutputHandler.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/VisualPluginOutputHandler.kt index d8023ab7..304d79c6 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/VisualPluginOutputHandler.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/plugin/output/VisualPluginOutputHandler.kt @@ -6,7 +6,7 @@ package com.github.serivesmejia.eocvsim.plugin.output import com.github.serivesmejia.eocvsim.util.event.ParamEventHandler -import io.github.deltacv.common.util.loggerOf +import org.deltacv.common.util.loggerOf import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.withTimeoutOrNull import kotlin.time.Duration.Companion.milliseconds diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.kt index 6085580d..966a4e41 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableField.kt @@ -9,7 +9,7 @@ import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanelConfig import com.github.serivesmejia.eocvsim.util.event.EventHandler -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualField import org.koin.core.component.KoinComponent import org.koin.core.component.inject diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldRegistry.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldRegistry.kt index d5261c91..030e123d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldRegistry.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunableFieldRegistry.kt @@ -7,7 +7,7 @@ package com.github.serivesmejia.eocvsim.tuner import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.util.ReflectUtil -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualField import com.github.serivesmejia.eocvsim.tuner.field.numeric.* import com.github.serivesmejia.eocvsim.tuner.field.* import com.github.serivesmejia.eocvsim.tuner.field.cv.* diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt index c8e1c303..6ebd4b9d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/TunerManager.kt @@ -7,10 +7,10 @@ package com.github.serivesmejia.eocvsim.tuner import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel import com.github.serivesmejia.eocvsim.tuner.exception.CancelTunableFieldAddingException -import io.github.deltacv.common.util.loggerForThis -import io.github.deltacv.eocvsim.virtualreflect.VirtualField -import io.github.deltacv.eocvsim.virtualreflect.VirtualReflection -import io.github.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflection +import org.deltacv.common.util.loggerForThis +import org.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualReflection +import org.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflection import org.koin.core.component.KoinComponent import org.koin.core.component.inject diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.kt index 7ed73161..9c8e6dab 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/BooleanField.kt @@ -8,7 +8,7 @@ package com.github.serivesmejia.eocvsim.tuner.field import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.tuner.TunableBoolean import com.github.serivesmejia.eocvsim.tuner.TunableField -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualField class BooleanField( instance: Any, diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/EnumField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/EnumField.kt index ba451815..9d59a577 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/EnumField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/EnumField.kt @@ -9,7 +9,7 @@ import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.tuner.TunableEnum import com.github.serivesmejia.eocvsim.tuner.TunableField import com.github.serivesmejia.eocvsim.tuner.TunableFieldAcceptor -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualField class EnumField(instance: Any, reflectionField: VirtualField) : TunableField>(instance, reflectionField, AllowMode.TEXT) { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt index 6f6ef73e..cfa3ee7e 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/NumericField.kt @@ -9,7 +9,7 @@ import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel import com.github.serivesmejia.eocvsim.tuner.TunableField import com.github.serivesmejia.eocvsim.tuner.TunableNumber -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualField abstract class NumericField( target: Any, diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.kt index be8d238e..7f0c89e4 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/StringField.kt @@ -9,7 +9,7 @@ import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel import com.github.serivesmejia.eocvsim.tuner.TunableField import com.github.serivesmejia.eocvsim.tuner.TunableString -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualField class StringField( instance: Any, diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.kt index 9bf35898..c4d74c8d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/PointField.kt @@ -8,7 +8,7 @@ package com.github.serivesmejia.eocvsim.tuner.field.cv import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.tuner.TunableField import com.github.serivesmejia.eocvsim.tuner.TunableNumber -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualField import org.opencv.core.Point class PointField( diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/RectField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/RectField.kt index 01285491..dc885fbf 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/RectField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/RectField.kt @@ -9,7 +9,7 @@ import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.tuner.TunableField import com.github.serivesmejia.eocvsim.tuner.TunableNumber import org.opencv.core.Rect -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualField class RectField(instance: Any, reflectionField: VirtualField) : TunableField(instance, reflectionField, AllowMode.ONLY_NUMBERS_DECIMAL) { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.kt index fa7c0f9b..48a50752 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/cv/ScalarField.kt @@ -9,7 +9,7 @@ import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.gui.component.tuner.TunableFieldPanel import com.github.serivesmejia.eocvsim.tuner.TunableField import com.github.serivesmejia.eocvsim.tuner.TunableNumber -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualField import org.opencv.core.Scalar class ScalarField( diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt index c060f2c8..2554c896 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/DoubleField.kt @@ -7,7 +7,7 @@ package com.github.serivesmejia.eocvsim.tuner.field.numeric import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.tuner.field.NumericField -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualField import com.github.serivesmejia.eocvsim.tuner.TunableFieldAcceptor class DoubleField( diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt index 3a1eb714..31107e30 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/FloatField.kt @@ -8,7 +8,7 @@ package com.github.serivesmejia.eocvsim.tuner.field.numeric import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.tuner.field.NumericField import com.github.serivesmejia.eocvsim.tuner.TunableFieldAcceptor -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualField class FloatField( instance: Any, diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt index 37ea5e57..25807576 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/IntegerField.kt @@ -7,7 +7,7 @@ package com.github.serivesmejia.eocvsim.tuner.field.numeric import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.tuner.field.NumericField -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualField import com.github.serivesmejia.eocvsim.tuner.TunableFieldAcceptor class IntegerField( diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt index 5e2b91c5..45999af4 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/tuner/field/numeric/LongField.kt @@ -8,7 +8,7 @@ package com.github.serivesmejia.eocvsim.tuner.field.numeric import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.tuner.field.NumericField import com.github.serivesmejia.eocvsim.tuner.TunableFieldAcceptor -import io.github.deltacv.eocvsim.virtualreflect.VirtualField +import org.deltacv.eocvsim.virtualreflect.VirtualField class LongField( instance: Any, diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt index 636b1c98..4c6b742b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/ClasspathScan.kt @@ -12,7 +12,7 @@ import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode import com.qualcomm.robotcore.eventloop.opmode.OpMode import com.qualcomm.robotcore.util.ElapsedTime import io.github.classgraph.ClassGraph -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import org.firstinspires.ftc.vision.VisionProcessor import org.openftc.easyopencv.OpenCvPipeline @@ -40,7 +40,7 @@ open class ClasspathScan { "org.opencv", "imgui", "io.github.classgraph", - "io.github.deltacv", + "org.deltacv", "com.github.serivesmejia.eocvsim.pipeline", "org.firstinspires.ftc.vision", "org.lwjgl", diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/compiler/CompilerProvider.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/compiler/CompilerProvider.kt index 0ed23961..5e471ab9 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/compiler/CompilerProvider.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/compiler/CompilerProvider.kt @@ -5,7 +5,7 @@ package com.github.serivesmejia.eocvsim.util.compiler -import io.github.deltacv.common.util.loggerOf +import org.deltacv.common.util.loggerOf import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler import javax.tools.JavaCompiler import javax.tools.ToolProvider diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt index 90ca50eb..cd3a92b3 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/CrashReport.kt @@ -11,7 +11,7 @@ import com.github.serivesmejia.eocvsim.util.StrUtil import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.extension.plus import com.github.serivesmejia.eocvsim.util.io.EOCVSimFolder -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import java.io.File import java.time.LocalDateTime import java.time.format.DateTimeFormatter diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/EOCVSimUncaughtExceptionHandler.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/EOCVSimUncaughtExceptionHandler.kt index dba1f503..3b3e36c2 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/EOCVSimUncaughtExceptionHandler.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/exception/handling/EOCVSimUncaughtExceptionHandler.kt @@ -9,7 +9,7 @@ import com.github.serivesmejia.eocvsim.currentMainThread import com.github.serivesmejia.eocvsim.gui.DialogFactory import com.github.serivesmejia.eocvsim.util.JavaProcess import com.github.serivesmejia.eocvsim.util.event.EventHandler -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/io/FileWatcher.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/io/FileWatcher.kt index 51e33e17..5d44cd57 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/io/FileWatcher.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/util/io/FileWatcher.kt @@ -6,7 +6,7 @@ package com.github.serivesmejia.eocvsim.util.io import com.github.serivesmejia.eocvsim.util.event.EventHandler -import io.github.deltacv.common.util.loggerOf +import org.deltacv.common.util.loggerOf import org.slf4j.Logger import java.io.File import java.nio.file.* diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt index 81e5af99..16da7664 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/WorkspaceManager.kt @@ -17,7 +17,7 @@ import com.github.serivesmejia.eocvsim.util.io.FileWatcher import com.github.serivesmejia.eocvsim.workspace.config.WorkspaceConfig import com.github.serivesmejia.eocvsim.workspace.config.WorkspaceConfigLoader import com.github.serivesmejia.eocvsim.workspace.util.WorkspaceTemplate -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/config/WorkspaceConfigLoader.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/config/WorkspaceConfigLoader.kt index f8487a06..e14c30fc 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/config/WorkspaceConfigLoader.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/config/WorkspaceConfigLoader.kt @@ -8,7 +8,7 @@ package com.github.serivesmejia.eocvsim.workspace.config import com.github.serivesmejia.eocvsim.Build import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.serialization.JacksonJsonSupport -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import java.io.File /** diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/VSCodeLauncher.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/VSCodeLauncher.kt index 667c4b91..3002151b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/VSCodeLauncher.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/VSCodeLauncher.kt @@ -10,7 +10,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import com.github.serivesmejia.eocvsim.util.SysUtil -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import kotlinx.coroutines.DelicateCoroutinesApi import java.io.File diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/WorkspaceTemplate.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/WorkspaceTemplate.kt index 30ff2eea..f3099bf5 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/WorkspaceTemplate.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/WorkspaceTemplate.kt @@ -5,7 +5,7 @@ package com.github.serivesmejia.eocvsim.workspace.util -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import java.io.File abstract class WorkspaceTemplate { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/DefaultWorkspaceTemplate.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/DefaultWorkspaceTemplate.kt index b111afd8..de28d0cf 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/DefaultWorkspaceTemplate.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/DefaultWorkspaceTemplate.kt @@ -6,7 +6,7 @@ package com.github.serivesmejia.eocvsim.workspace.util.template import com.github.serivesmejia.eocvsim.util.SysUtil -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import com.github.serivesmejia.eocvsim.workspace.util.WorkspaceTemplate import net.lingala.zip4j.ZipFile import java.io.File diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/GradleWorkspaceTemplate.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/GradleWorkspaceTemplate.kt index c81a5af8..cfa5ab3d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/GradleWorkspaceTemplate.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/GradleWorkspaceTemplate.kt @@ -7,7 +7,7 @@ package com.github.serivesmejia.eocvsim.workspace.util.template import com.github.serivesmejia.eocvsim.Build import com.github.serivesmejia.eocvsim.util.SysUtil -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import com.github.serivesmejia.eocvsim.workspace.util.WorkspaceTemplate import net.lingala.zip4j.ZipFile import java.io.File diff --git a/EOCV-Sim/src/main/java/com/qualcomm/robotcore/eventloop/opmode/OpModePipelineHandler.kt b/EOCV-Sim/src/main/java/com/qualcomm/robotcore/eventloop/opmode/OpModePipelineHandler.kt index 8dddfae1..c65febb5 100644 --- a/EOCV-Sim/src/main/java/com/qualcomm/robotcore/eventloop/opmode/OpModePipelineHandler.kt +++ b/EOCV-Sim/src/main/java/com/qualcomm/robotcore/eventloop/opmode/OpModePipelineHandler.kt @@ -10,8 +10,8 @@ import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.github.serivesmejia.eocvsim.pipeline.handler.SpecificPipelineHandler import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.qualcomm.robotcore.hardware.HardwareMap -import io.github.deltacv.eocvsim.input.VisionInputSourceProvider -import io.github.deltacv.vision.external.source.ThreadVisionSourceProvider +import org.deltacv.eocvsim.input.VisionInputSourceProvider +import org.deltacv.vision.external.source.ThreadVisionSourceProvider import org.firstinspires.ftc.robotcore.external.Telemetry import org.openftc.easyopencv.OpenCvPipeline import org.openftc.easyopencv.OpenCvViewport diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/MethodCallByteCodeChecker.kt b/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/MethodCallByteCodeChecker.kt deleted file mode 100644 index e634417a..00000000 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/sandbox/restrictions/MethodCallByteCodeChecker.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - -package io.github.deltacv.eocvsim.sandbox.restrictions - -import org.objectweb.asm.ClassReader -import org.objectweb.asm.ClassVisitor -import org.objectweb.asm.MethodVisitor -import org.objectweb.asm.Opcodes - -class MethodCallByteCodeChecker( - bytecode: ByteArray, - val methodBlacklist: Set -) { - - init { - // Create a ClassReader to read the bytecode - val classReader = ClassReader(bytecode) - // Accept the ClassVisitor to visit the class - classReader.accept(MethodCheckClassVisitor(), 0) - } - - private inner class MethodCheckClassVisitor : ClassVisitor(Opcodes.ASM9) { - lateinit var currentClassName: String - - override fun visit( - version: Int, - access: Int, - name: String?, - signature: String?, - superName: String?, - interfaces: Array? - ) { - super.visit(version, access, name, signature, superName, interfaces) - currentClassName = name!!.replace("/", ".") - } - - override fun visitMethod( - access: Int, - name: String?, - descriptor: String?, - signature: String?, - exceptions: Array? - ): MethodVisitor { - val mv = super.visitMethod(access, name, descriptor, signature, exceptions) - return MethodCheckMethodVisitor(mv, currentClassName, name!!) - } - } - - private inner class MethodCheckMethodVisitor(mv: MethodVisitor?, - val currentClassName: String, val currentMethodName: String) : MethodVisitor(Opcodes.ASM9, mv) { - override fun visitMethodInsn( - opcode: Int, - owner: String?, - name: String?, - descriptor: String?, - isInterface: Boolean - ) { - super.visitMethodInsn(opcode, owner, name, descriptor, isInterface) - val methodIdentifier = "${owner!!.replace("/", ".")}#$name" - - if (methodBlacklist.contains(methodIdentifier)) { - throw IllegalAccessError("Unauthorized method call of $methodIdentifier from dynamic code at $currentClassName#$currentMethodName") - } - } - } - -} diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/gui/dialog/SuperAccessRequest.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/gui/dialog/SuperAccessRequest.kt similarity index 96% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/gui/dialog/SuperAccessRequest.kt rename to EOCV-Sim/src/main/java/org/deltacv/eocvsim/gui/dialog/SuperAccessRequest.kt index bec48029..3b476107 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/gui/dialog/SuperAccessRequest.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/gui/dialog/SuperAccessRequest.kt @@ -1,9 +1,9 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - -package io.github.deltacv.eocvsim.gui.dialog +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.deltacv.eocvsim.gui.dialog import com.formdev.flatlaf.FlatLightLaf import com.formdev.flatlaf.intellijthemes.FlatArcDarkIJTheme diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSource.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/input/VisionInputSource.kt similarity index 79% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSource.kt rename to EOCV-Sim/src/main/java/org/deltacv/eocvsim/input/VisionInputSource.kt index e696b4b2..15f9117b 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSource.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/input/VisionInputSource.kt @@ -3,15 +3,15 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.input +package org.deltacv.eocvsim.input import com.github.serivesmejia.eocvsim.input.InputSource import com.github.serivesmejia.eocvsim.input.source.CameraSource -import io.github.deltacv.common.util.loggerForThis -import io.github.deltacv.eocvsim.input.control.CameraSourceControlMap -import io.github.deltacv.vision.external.source.VisionSourceBase -import io.github.deltacv.vision.external.util.ThrowableHandler -import io.github.deltacv.vision.external.util.Timestamped +import org.deltacv.common.util.loggerForThis +import org.deltacv.eocvsim.input.control.CameraSourceControlMap +import org.deltacv.vision.external.source.VisionSourceBase +import org.deltacv.vision.external.util.ThrowableHandler +import org.deltacv.vision.external.util.Timestamped import org.opencv.core.Mat import org.opencv.core.Size diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/input/VisionInputSourceProvider.kt similarity index 86% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt rename to EOCV-Sim/src/main/java/org/deltacv/eocvsim/input/VisionInputSourceProvider.kt index 6402da70..5ef85e79 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/VisionInputSourceProvider.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/input/VisionInputSourceProvider.kt @@ -3,17 +3,17 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.input +package org.deltacv.eocvsim.input import com.github.serivesmejia.eocvsim.input.InputSourceManager import com.github.serivesmejia.eocvsim.input.source.CameraSource import com.github.serivesmejia.eocvsim.input.source.ImageSource import com.github.serivesmejia.eocvsim.input.source.VideoSource -import io.github.deltacv.vision.external.source.ViewportVisionSourceProvider -import io.github.deltacv.vision.external.source.VisionSource -import io.github.deltacv.vision.internal.opmode.OpModeNotifier -import io.github.deltacv.vision.internal.opmode.OpModeState -import io.github.deltacv.vision.internal.opmode.RedirectToOpModeThrowableHandler +import org.deltacv.vision.external.source.ViewportVisionSourceProvider +import org.deltacv.vision.external.source.VisionSource +import org.deltacv.vision.internal.opmode.OpModeNotifier +import org.deltacv.vision.internal.opmode.OpModeState +import org.deltacv.vision.internal.opmode.RedirectToOpModeThrowableHandler import org.opencv.core.Mat import org.opencv.core.Size import org.openftc.easyopencv.OpenCvViewport diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceControlMap.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/input/control/CameraSourceControlMap.kt similarity index 83% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceControlMap.kt rename to EOCV-Sim/src/main/java/org/deltacv/eocvsim/input/control/CameraSourceControlMap.kt index 5a215fcf..1b45ba62 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceControlMap.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/input/control/CameraSourceControlMap.kt @@ -1,12 +1,12 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - -package io.github.deltacv.eocvsim.input.control +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.deltacv.eocvsim.input.control import com.github.serivesmejia.eocvsim.input.source.CameraSource -import io.github.deltacv.vision.external.source.CameraControlMap +import org.deltacv.vision.external.source.CameraControlMap import org.firstinspires.ftc.robotcore.external.hardware.camera.controls.CameraControl import org.firstinspires.ftc.robotcore.external.hardware.camera.controls.ExposureControl diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt similarity index 95% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt rename to EOCV-Sim/src/main/java/org/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt index 9dec5a81..1a99159c 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/input/control/CameraSourceExposureControl.kt @@ -1,9 +1,9 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - -package io.github.deltacv.eocvsim.input.control +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.deltacv.eocvsim.input.control import com.github.serivesmejia.eocvsim.input.source.CameraSource import org.firstinspires.ftc.robotcore.external.hardware.camera.controls.ExposureControl diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/pipeline/StreamableOpenCvPipelineInstantiator.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/pipeline/StreamableOpenCvPipelineInstantiator.kt similarity index 83% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/pipeline/StreamableOpenCvPipelineInstantiator.kt rename to EOCV-Sim/src/main/java/org/deltacv/eocvsim/pipeline/StreamableOpenCvPipelineInstantiator.kt index 6d00d0b1..43cdbdfd 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/pipeline/StreamableOpenCvPipelineInstantiator.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/pipeline/StreamableOpenCvPipelineInstantiator.kt @@ -3,12 +3,12 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.pipeline +package org.deltacv.eocvsim.pipeline import com.github.serivesmejia.eocvsim.pipeline.instantiator.DefaultPipelineInstantiator import com.github.serivesmejia.eocvsim.pipeline.instantiator.PipelineInstantiator -import io.github.deltacv.eocvsim.stream.ImageStreamer -import io.github.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflection +import org.deltacv.eocvsim.stream.ImageStreamer +import org.deltacv.eocvsim.virtualreflect.jvm.JvmVirtualReflection import org.firstinspires.ftc.robotcore.external.Telemetry import org.openftc.easyopencv.OpenCvPipeline @@ -27,4 +27,4 @@ class StreamableOpenCvPipelineInstantiator( override fun variableTunerTarget(pipeline: OpenCvPipeline) = pipeline -} +} diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/EmbeddedPluginLoader.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/loader/EmbeddedPluginLoader.kt similarity index 89% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/EmbeddedPluginLoader.kt rename to EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/loader/EmbeddedPluginLoader.kt index 2fd93bde..caaccf59 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/EmbeddedPluginLoader.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/loader/EmbeddedPluginLoader.kt @@ -1,17 +1,17 @@ -/* - * Copyright (c) 2026 Sebastian Erives - * Licensed under the MIT License. - */ - -package io.github.deltacv.eocvsim.plugin.loader +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.deltacv.eocvsim.plugin.loader import com.github.serivesmejia.eocvsim.config.ConfigLoader import com.github.serivesmejia.eocvsim.util.extension.plus -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.ApiDisabler -import io.github.deltacv.eocvsim.plugin.api.EOCVSimApi -import io.github.deltacv.eocvsim.plugin.security.PluginSignature -import io.github.deltacv.eocvsim.sandbox.nio.SandboxFileSystem +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.api.ApiDisabler +import org.deltacv.eocvsim.plugin.api.EOCVSimApi +import org.deltacv.eocvsim.plugin.security.PluginSignature +import org.deltacv.eocvsim.sandbox.nio.SandboxFileSystem import net.lingala.zip4j.ZipFile import java.io.File import java.nio.file.Path diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt similarity index 79% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt rename to EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt index 43f234d2..12862e44 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt @@ -1,9 +1,9 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - -package io.github.deltacv.eocvsim.plugin.loader +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.deltacv.eocvsim.plugin.loader import com.github.serivesmejia.eocvsim.EOCVSim import com.github.serivesmejia.eocvsim.config.ConfigLoader @@ -12,15 +12,15 @@ import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.util.extension.hashString import com.github.serivesmejia.eocvsim.util.extension.plus -import io.github.deltacv.common.util.loggerForThis -import com.moandjiezana.toml.Toml -import io.github.deltacv.common.util.ParsedVersion -import io.github.deltacv.eocvsim.plugin.EMBEDDED_PLUGIN_FOLDER -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.api.ApiDisabler -import io.github.deltacv.eocvsim.plugin.api.EOCVSimApi -import io.github.deltacv.eocvsim.plugin.security.PluginSignatureVerifier -import io.github.deltacv.eocvsim.sandbox.nio.SandboxFileSystem +import org.deltacv.common.util.loggerForThis +import org.deltacv.common.util.Toml +import org.deltacv.common.util.ParsedVersion +import org.deltacv.eocvsim.plugin.EMBEDDED_PLUGIN_FOLDER +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.api.ApiDisabler +import org.deltacv.eocvsim.plugin.api.EOCVSimApi +import org.deltacv.eocvsim.plugin.security.PluginSignatureVerifier +import org.deltacv.eocvsim.sandbox.nio.SandboxFileSystem import net.lingala.zip4j.ZipFile import java.io.File import java.nio.file.Path @@ -102,7 +102,25 @@ open class FilePluginLoaderImpl( ?: throw InvalidPluginException("No plugin.toml in the jar file") ) - pluginInfo = PluginInfo.fromToml(pluginToml) + // Parse plugin info directly to avoid cross-module Toml type mismatch + val name = pluginToml.getString("name")?.trim() + ?: throw InvalidPluginException("No name in plugin.toml") + + val version = pluginToml.getString("version")?.trim() + ?: throw InvalidPluginException("No version in plugin.toml") + + val author = pluginToml.getString("author")?.trim() + ?: throw InvalidPluginException("No author in plugin.toml") + + val authorEmail = pluginToml.getString("author-email")?.trim() ?: "" + + val main = pluginToml.getString("main")?.trim() + ?: throw InvalidPluginException("No main in plugin.toml") + + val description = pluginToml.getString("description")?.trim() ?: "" + val superAccess = pluginToml.getBoolean("super-access") ?: false + + pluginInfo = PluginInfo(name, version, author, authorEmail, main, description, superAccess) } /** @@ -126,9 +144,9 @@ open class FilePluginLoaderImpl( setupFs() - if(pluginToml.contains("api-version") || pluginToml.contains("min-api-version")) { + if(pluginToml.getString("api-version") != null || pluginToml.getString("min-api-version") != null) { // default to api-version if min-api-version is not present - val apiVersionKey = if(pluginToml.contains("api-version")) "api-version" else "min-api-version" + val apiVersionKey = if(pluginToml.getString("api-version") != null) "api-version" else "min-api-version" val parsedVersion = ParsedVersion(pluginToml.getString(apiVersionKey)) if(parsedVersion > EOCVSim.PARSED_VERSION) @@ -137,7 +155,7 @@ open class FilePluginLoaderImpl( logger.info("Plugin ${pluginInfo.name} requests min api version of v${parsedVersion}") } - if(pluginToml.contains("max-api-version")) { + if(pluginToml.getString("max-api-version") != null) { val parsedVersion = ParsedVersion(pluginToml.getString("max-api-version")) if(parsedVersion < EOCVSim.PARSED_VERSION) @@ -146,7 +164,7 @@ open class FilePluginLoaderImpl( logger.info("Plugin ${pluginInfo.name} requests max api version of v${parsedVersion}") } - if(pluginToml.contains("exact-api-version")) { + if(pluginToml.getString("exact-api-version") != null) { val parsedVersion = ParsedVersion(pluginToml.getString("exact-api-version")) if(parsedVersion != EOCVSim.PARSED_VERSION) @@ -155,7 +173,7 @@ open class FilePluginLoaderImpl( logger.info("Plugin ${pluginInfo.name} requests exact api version of v${parsedVersion}") } - if(pluginToml.getBoolean("super-access", false)) { + if(pluginToml.getBoolean("super-access") != null && pluginToml.getBoolean("super-access")) { requestSuperAccess(pluginToml.getString("super-access-reason", "")) } diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginClassLoader.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/loader/PluginClassLoader.kt similarity index 91% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginClassLoader.kt rename to EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/loader/PluginClassLoader.kt index 33b6e9d3..f6f9247d 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginClassLoader.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/loader/PluginClassLoader.kt @@ -3,16 +3,16 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.loader +package org.deltacv.eocvsim.plugin.loader import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.extension.removeFromEnd -import io.github.deltacv.eocvsim.sandbox.restrictions.MethodCallByteCodeChecker -import io.github.deltacv.eocvsim.sandbox.restrictions.dynamicCodeExactMatchBlacklist -import io.github.deltacv.eocvsim.sandbox.restrictions.dynamicCodeMethodBlacklist -import io.github.deltacv.eocvsim.sandbox.restrictions.dynamicCodePackageAlwaysBlacklist -import io.github.deltacv.eocvsim.sandbox.restrictions.dynamicCodePackageBlacklist -import io.github.deltacv.eocvsim.sandbox.restrictions.dynamicCodePackageWhitelist +import org.deltacv.eocvsim.sandbox.restrictions.MethodCallByteCodeChecker +import org.deltacv.eocvsim.sandbox.restrictions.dynamicCodeExactMatchBlacklist +import org.deltacv.eocvsim.sandbox.restrictions.dynamicCodeMethodBlacklist +import org.deltacv.eocvsim.sandbox.restrictions.dynamicCodePackageAlwaysBlacklist +import org.deltacv.eocvsim.sandbox.restrictions.dynamicCodePackageBlacklist +import org.deltacv.eocvsim.sandbox.restrictions.dynamicCodePackageWhitelist import java.io.ByteArrayOutputStream import java.lang.ref.WeakReference import java.io.File diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginManager.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/loader/PluginManager.kt similarity index 89% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginManager.kt rename to EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/loader/PluginManager.kt index 85026719..09fab0d6 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/loader/PluginManager.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/loader/PluginManager.kt @@ -1,9 +1,9 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - -package io.github.deltacv.eocvsim.plugin.loader +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.deltacv.eocvsim.plugin.loader import com.github.serivesmejia.eocvsim.Build import com.github.serivesmejia.eocvsim.LifecycleSignal @@ -15,12 +15,12 @@ import com.github.serivesmejia.eocvsim.plugin.output.PluginOutputHandler import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.util.orchestration.initDependency import com.github.serivesmejia.eocvsim.util.orchestration.PhaseOrchestrableBase -import io.github.deltacv.common.util.loggerForThis -import io.github.deltacv.eocvsim.plugin.EOCVSimPlugin -import io.github.deltacv.eocvsim.plugin.repository.PluginRepositoryManager -import io.github.deltacv.eocvsim.plugin.security.superaccess.SuperAccessDaemon -import io.github.deltacv.eocvsim.plugin.security.superaccess.SuperAccessDaemonClient -import io.github.deltacv.eocvsim.plugin.security.toMutable +import org.deltacv.common.util.loggerForThis +import org.deltacv.eocvsim.plugin.EOCVSimPlugin +import org.deltacv.eocvsim.plugin.repository.PluginRepositoryManager +import org.deltacv.eocvsim.plugin.security.superaccess.SuperAccessDaemon +import org.deltacv.eocvsim.plugin.security.superaccess.SuperAccessDaemonClient +import org.deltacv.eocvsim.plugin.security.toMutable import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.runBlocking import org.koin.core.component.KoinComponent @@ -42,9 +42,9 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { private val lifecycleChannel: Channel by inject(named("lifecycle")) companion object { - 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 + val PLUGIN_FOLDER = org.deltacv.eocvsim.plugin.PLUGIN_FOLDER + val PLUGIN_CACHING_FOLDER = org.deltacv.eocvsim.plugin.PLUGIN_CACHING_FOLDER + val FILESYSTEMS_FOLDER = org.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." @@ -98,8 +98,6 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { outputHandler.sendOutputLine("Initializing PluginManager") - superAccessDaemonClient.init() - // replace papervision line if (!configManager.config.flags.getOrDefault("hasDiscardedPaperVisionRepository", false)) { @@ -134,14 +132,14 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { _pluginFiles.addAll(repositoryManager.resolveAll()) - if (configManager.config.flags.getOrDefault("startFresh", false)) { - logger.warn("startFresh = true, deleting all plugins in the plugins folder") + if (configManager.config.flags.getOrDefault("startFreshPlugins", false)) { + logger.warn("startFreshPlugins = true, deleting all plugins in the plugins folder") for (file in pluginFilesInFolder) { file.delete() } - configManager.config.flags["startFresh"] = false + configManager.config.flags["startFreshPlugins"] = false configManager.saveToFile() } else { _pluginFiles.addAll(pluginFilesInFolder) @@ -192,7 +190,7 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { Build.paperVisionVersion, "deltacv", "dev@deltacv.org", - "io.github.deltacv.papervision.plugin.PaperVisionEOCVSimPlugin", + "org.deltacv.papervision.plugin.PaperVisionEOCVSimPlugin", "Create your custom OpenCV algorithms using a user-friendly node editor interface", true ) @@ -200,7 +198,7 @@ class PluginManager : PhaseOrchestrableBase(), KoinComponent { @Suppress("UNCHECKED_CAST") addEmbeddedPlugin( pluginInfo, - Class.forName("io.github.deltacv.papervision.plugin.PaperVisionEOCVSimPlugin") as Class, + Class.forName("org.deltacv.papervision.plugin.PaperVisionEOCVSimPlugin") as Class, ) logger.info("Loaded embedded PaperVision from built-in class") diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt similarity index 95% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt rename to EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt index 6b892cb5..55b617c8 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt @@ -1,9 +1,9 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - -package io.github.deltacv.eocvsim.plugin.repository +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.deltacv.eocvsim.plugin.repository import com.github.serivesmejia.eocvsim.plugin.output.PluginDialogSignal import com.github.serivesmejia.eocvsim.plugin.output.PluginOutputHandler @@ -11,10 +11,10 @@ import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.util.extension.hashString import com.github.serivesmejia.eocvsim.util.extension.plus -import com.moandjiezana.toml.Toml -import io.github.deltacv.common.util.ParsedVersion -import io.github.deltacv.common.util.loggerForThis -import io.github.deltacv.eocvsim.plugin.loader.PluginManager +import org.deltacv.common.util.Toml +import org.deltacv.common.util.ParsedVersion +import org.deltacv.common.util.loggerForThis +import org.deltacv.eocvsim.plugin.loader.PluginManager import kotlinx.coroutines.runBlocking import org.jboss.shrinkwrap.resolver.api.maven.ConfigurableMavenResolverSystem import org.jboss.shrinkwrap.resolver.api.maven.Maven diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryUpdateChecker.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/repository/PluginRepositoryUpdateChecker.kt similarity index 94% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryUpdateChecker.kt rename to EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/repository/PluginRepositoryUpdateChecker.kt index edbc57ce..f48d9306 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/repository/PluginRepositoryUpdateChecker.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/repository/PluginRepositoryUpdateChecker.kt @@ -3,10 +3,10 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.plugin.repository +package org.deltacv.eocvsim.plugin.repository -import io.github.deltacv.common.util.loggerOf -import io.github.deltacv.common.util.ParsedVersion +import org.deltacv.common.util.loggerOf +import org.deltacv.common.util.ParsedVersion import java.net.URI import java.net.URL import javax.xml.parsers.DocumentBuilderFactory diff --git a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemon.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemon.kt similarity index 81% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemon.kt rename to EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemon.kt index fb33b756..71ba4410 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemon.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemon.kt @@ -1,24 +1,24 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - -package io.github.deltacv.eocvsim.plugin.security.superaccess +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.deltacv.eocvsim.plugin.security.superaccess import com.github.serivesmejia.eocvsim.util.extension.fileHash import com.github.serivesmejia.eocvsim.util.extension.plus import com.github.serivesmejia.eocvsim.util.serialization.JacksonJsonSupport -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import com.fasterxml.jackson.annotation.JsonTypeInfo -import com.moandjiezana.toml.Toml -import io.github.deltacv.eocvsim.gui.dialog.SuperAccessRequest -import io.github.deltacv.eocvsim.plugin.loader.PluginInfo -import io.github.deltacv.eocvsim.plugin.loader.PluginManager -import io.github.deltacv.eocvsim.plugin.loader.PluginManager.Companion.GENERIC_LAWYER_YEET -import io.github.deltacv.eocvsim.plugin.loader.PluginManager.Companion.GENERIC_SUPERACCESS_WARN -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.deltacv.common.util.Toml +import org.deltacv.eocvsim.gui.dialog.SuperAccessRequest +import org.deltacv.eocvsim.plugin.loader.PluginInfo +import org.deltacv.eocvsim.plugin.loader.PluginManager +import org.deltacv.eocvsim.plugin.loader.PluginManager.Companion.GENERIC_LAWYER_YEET +import org.deltacv.eocvsim.plugin.loader.PluginManager.Companion.GENERIC_SUPERACCESS_WARN +import org.deltacv.eocvsim.plugin.security.Authority +import org.deltacv.eocvsim.plugin.security.AuthorityFetcher +import org.deltacv.eocvsim.plugin.security.MutablePluginSignature import org.java_websocket.client.WebSocketClient import org.java_websocket.handshake.ServerHandshake import java.io.File @@ -26,8 +26,10 @@ import java.lang.Exception import java.net.URI import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.Executors +import java.util.concurrent.locks.ReentrantLock import java.util.zip.ZipFile import javax.swing.SwingUtilities +import kotlin.concurrent.withLock import kotlin.system.exitProcess object SuperAccessDaemon { @@ -56,6 +58,7 @@ object SuperAccessDaemon { val SUPERACCESS_FILE = PluginManager.PLUGIN_CACHING_FOLDER + File.separator + "superaccess.txt" private val access = ConcurrentHashMap() + private val fileLock = ReentrantLock() @JvmStatic fun main(args: Array) { @@ -110,7 +113,11 @@ object SuperAccessDaemon { return@handleRequest } - if(SUPERACCESS_FILE.exists() && SUPERACCESS_FILE.readLines().contains(pluginFile.fileHash())) { + val hasAccess = fileLock.withLock { + SUPERACCESS_FILE.exists() && SUPERACCESS_FILE.readLines().contains(pluginFile.fileHash()) + } + + if(hasAccess) { accessGranted(message.id, message.pluginPath) return } @@ -153,7 +160,15 @@ object SuperAccessDaemon { // helper function to grant access, avoid code duplication fun grant() { - SUPERACCESS_FILE.appendText(pluginFile.fileHash() + "\n") + fileLock.withLock { + if (!SUPERACCESS_FILE.exists()) { + SUPERACCESS_FILE.createNewFile() + } + // Re-check in case another thread granted access in the meantime + if (!SUPERACCESS_FILE.readLines().contains(pluginFile.fileHash())) { + SUPERACCESS_FILE.appendText(pluginFile.fileHash() + "\n") + } + } accessGranted(message.id, message.pluginPath) } @@ -197,7 +212,11 @@ object SuperAccessDaemon { val pluginFile = File(message.pluginPath) - if(SUPERACCESS_FILE.exists() && SUPERACCESS_FILE.readLines().contains(pluginFile.fileHash())) { + val hasAccess = fileLock.withLock { + SUPERACCESS_FILE.exists() && SUPERACCESS_FILE.readLines().contains(pluginFile.fileHash()) + } + + if(hasAccess) { accessGranted(message.id, message.pluginPath) } 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/org/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemonClient.kt similarity index 86% rename from EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemonClient.kt rename to EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemonClient.kt index 26ba50dc..7bf7294f 100644 --- a/EOCV-Sim/src/main/java/io/github/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemonClient.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemonClient.kt @@ -1,13 +1,13 @@ -/* - * Copyright (c) 2024 Sebastian Erives - * Licensed under the MIT License. - */ - -package io.github.deltacv.eocvsim.plugin.security.superaccess +/* + * Copyright (c) 2024 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.deltacv.eocvsim.plugin.security.superaccess import com.github.serivesmejia.eocvsim.util.JavaProcess import com.github.serivesmejia.eocvsim.util.serialization.JacksonJsonSupport -import io.github.deltacv.common.util.loggerForThis +import org.deltacv.common.util.loggerForThis import org.java_websocket.WebSocket import org.java_websocket.handshake.ClientHandshake import org.java_websocket.server.WebSocketServer @@ -30,7 +30,7 @@ private data class AccessCache( class SuperAccessDaemonClient( val cacheTTLMillis: Long = 3_000, - autoacceptOnTrusted: Boolean + val autoacceptOnTrusted: Boolean ) { val logger by loggerForThis() @@ -42,27 +42,35 @@ class SuperAccessDaemonClient( private val accessCache = mutableMapOf() // create a new WebSocket server - private val server = WsServer(startLock, startCondition, autoacceptOnTrusted) + private var server: WsServer? = null + + private var isInitialized = false - fun init() { - server.start() + fun initIfNeeded() { + if (isInitialized) return + + server = WsServer(startLock, startCondition, autoacceptOnTrusted) + server!!.start() startLock.withLock { startCondition.await() } logger.info("SuperAccessDaemonClient initialized") + isInitialized = true } fun sendRequest(request: SuperAccessDaemon.SuperAccessMessage.Request, onResponse: (Boolean) -> Unit) { - if(server.connections.isEmpty()) { + initIfNeeded() + + if(server!!.connections.isEmpty()) { onResponse(false) return } - server.broadcast(JacksonJsonSupport.ipcMapper.writeValueAsString(request)) + server!!.broadcast(JacksonJsonSupport.ipcMapper.writeValueAsString(request)) - server.addResponseReceiver(request.id) { response -> + server!!.addResponseReceiver(request.id) { response -> val result = when (response) { is SuperAccessDaemon.SuperAccessResponse.Success -> { onResponse(true) @@ -92,16 +100,18 @@ class SuperAccessDaemonClient( } } } + + initIfNeeded() val lock = ReentrantLock() val condition = lock.newCondition() val check = SuperAccessDaemon.SuperAccessMessage.Check(file.absolutePath) - server.broadcast(JacksonJsonSupport.ipcMapper.writeValueAsString(check)) + server!!.broadcast(JacksonJsonSupport.ipcMapper.writeValueAsString(check)) var hasAccess = false - server.addResponseReceiver(check.id) { response -> + server!!.addResponseReceiver(check.id) { response -> if(response is SuperAccessDaemon.SuperAccessResponse.Success) { hasAccess = true @@ -154,7 +164,7 @@ class SuperAccessDaemonClient( } override fun onOpen(conn: WebSocket, p1: ClientHandshake?) { - val hostString = conn.localSocketAddress.hostString + val hostString = conn.remoteSocketAddress.hostString if(hostString != "127.0.0.1" && hostString != "localhost" && hostString != "0.0.0.0") { logger.warn("Connection from ${conn.remoteSocketAddress} refused, only localhost connections are allowed") conn.close(1013, "Ipc does not allow connections incoming from non-localhost addresses") diff --git a/EOCV-Sim/src/main/java/org/deltacv/eocvsim/sandbox/restrictions/MethodCallByteCodeChecker.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/sandbox/restrictions/MethodCallByteCodeChecker.kt new file mode 100644 index 00000000..d1644491 --- /dev/null +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/sandbox/restrictions/MethodCallByteCodeChecker.kt @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2026 Sebastian Erives + * Licensed under the MIT License. + */ + +package org.deltacv.eocvsim.sandbox.restrictions + +import org.objectweb.asm.* + +class MethodCallByteCodeChecker( + bytecode: ByteArray, + private val methodBlacklist: Set +) { + + init { + val classReader = ClassReader(bytecode) + classReader.accept(MethodCheckClassVisitor(), 0) + } + + private inner class MethodCheckClassVisitor : ClassVisitor(Opcodes.ASM9) { + + private lateinit var currentClassName: String + + override fun visit( + version: Int, + access: Int, + name: String?, + signature: String?, + superName: String?, + interfaces: Array? + ) { + super.visit(version, access, name, signature, superName, interfaces) + + currentClassName = name!!.replace('/', '.') + } + + override fun visitMethod( + access: Int, + name: String?, + descriptor: String?, + signature: String?, + exceptions: Array? + ): MethodVisitor { + + val mv = super.visitMethod( + access, + name, + descriptor, + signature, + exceptions + ) + + return MethodCheckMethodVisitor( + mv, + currentClassName, + name!! + ) + } + } + + private inner class MethodCheckMethodVisitor( + mv: MethodVisitor?, + private val currentClassName: String, + private val currentMethodName: String + ) : MethodVisitor(Opcodes.ASM9, mv) { + + private fun checkMethod(owner: String, name: String) { + val methodIdentifier = + "${owner.replace('/', '.')}#$name" + + if(methodBlacklist.contains(methodIdentifier)) { + throw IllegalAccessError( + "Unauthorized method call of $methodIdentifier " + + "from dynamic code at " + + "$currentClassName#$currentMethodName" + ) + } + } + + override fun visitMethodInsn( + opcode: Int, + owner: String?, + name: String?, + descriptor: String?, + isInterface: Boolean + ) { + super.visitMethodInsn( + opcode, + owner, + name, + descriptor, + isInterface + ) + + checkMethod(owner!!, name!!) + } + + override fun visitInvokeDynamicInsn( + name: String?, + descriptor: String?, + bootstrapMethodHandle: Handle?, + bootstrapMethodArguments: Array? + ) { + + super.visitInvokeDynamicInsn( + name, + descriptor, + bootstrapMethodHandle, + bootstrapMethodArguments + ) + + /* + * Check bootstrap method itself + */ + bootstrapMethodHandle?.let { + checkMethod(it.owner, it.name) + } + + /* + * Check bootstrap arguments + * + * Lambdas and method references commonly store + * target methods here as Handle instances. + */ + bootstrapMethodArguments?.forEach { arg -> + + when(arg) { + + is Handle -> { + checkMethod(arg.owner, arg.name) + } + + is ConstantDynamic -> { + + /* + * ConstantDynamic also has a bootstrap + * method and bootstrap args. + */ + + val bsm = arg.bootstrapMethod + + checkMethod( + bsm.owner, + bsm.name + ) + + for(i in 0 until arg.bootstrapMethodArgumentCount) { + + val nestedArg = + arg.getBootstrapMethodArgument(i) + + if(nestedArg is Handle) { + checkMethod( + nestedArg.owner, + nestedArg.name + ) + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/EOCV-Sim/src/main/resources/contributors.txt b/EOCV-Sim/src/main/resources/contributors.txt index a57bd0ce..b6c75cd8 100644 --- a/EOCV-Sim/src/main/resources/contributors.txt +++ b/EOCV-Sim/src/main/resources/contributors.txt @@ -1,5 +1,5 @@ -NPE (Windwoes) - EasyOpenCV and AprilTag Plugin -serivesmejia - Main deltacv Dev +serivesmejia - Main Dev @ deltacv +NPE (Windwoes) - Original EasyOpenCV & AprilTag Plugin Purav - Contributor & Mac Tester Jaran - Kotlin & Coroutines Advisor Shaurya - Guide Contributor & Mac Tester diff --git a/EOCV-Sim/src/main/resources/opensourcelibs.txt b/EOCV-Sim/src/main/resources/opensourcelibs.txt index 2adc8109..bc932b6d 100644 --- a/EOCV-Sim/src/main/resources/opensourcelibs.txt +++ b/EOCV-Sim/src/main/resources/opensourcelibs.txt @@ -1,10 +1,10 @@ EOCV-Sim and its source code is distributed under the MIT License OpenCV - Under Apache 2.0 License -OpenPnP OpenCV - Under Apache 2.0 License +WPILib - Under BSD 3-Clause License FTC SDK - Some source code under BSD License EasyOpenCV - Some source code under MIT License -EOCV-AprilTag-Plugin - Source code under MIT License +EOCV-AprilTag-Plugin - API under the MIT License Skiko - Under Apache 2.0 License Gson - Under Apache 2.0 License ClassGraph - Under MIT License diff --git a/Vision/build.gradle b/Vision/build.gradle index f517f1f9..43266026 100644 --- a/Vision/build.gradle +++ b/Vision/build.gradle @@ -13,9 +13,9 @@ dependencies { implementation project(':Common') compileOnly wpilibTools.deps.wpilibOpenCvJava(opencvVersion) + compileOnly wpilibTools.deps.wpilibJava("apriltag") compileOnly wpilibTools.deps.wpilibJava("wpiutil") compileOnly wpilibTools.deps.wpilibJava("wpimath") - compileOnly wpilibTools.deps.wpilibJava("apriltag") implementation "org.slf4j:slf4j-api:$slf4j_version" implementation 'org.jetbrains.kotlin:kotlin-stdlib' diff --git a/Vision/src/main/java/com/qualcomm/robotcore/eventloop/opmode/LinearOpMode.java b/Vision/src/main/java/com/qualcomm/robotcore/eventloop/opmode/LinearOpMode.java index 24571c5a..5703319c 100644 --- a/Vision/src/main/java/com/qualcomm/robotcore/eventloop/opmode/LinearOpMode.java +++ b/Vision/src/main/java/com/qualcomm/robotcore/eventloop/opmode/LinearOpMode.java @@ -31,8 +31,8 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE package com.qualcomm.robotcore.eventloop.opmode; -import io.github.deltacv.vision.external.source.ThreadVisionSourceProvider; -import io.github.deltacv.vision.external.source.VisionSourceProvider; +import org.deltacv.vision.external.source.ThreadVisionSourceProvider; +import org.deltacv.vision.external.source.VisionSourceProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/Vision/src/main/java/com/qualcomm/robotcore/eventloop/opmode/OpMode.java b/Vision/src/main/java/com/qualcomm/robotcore/eventloop/opmode/OpMode.java index 176b34ae..724ab948 100644 --- a/Vision/src/main/java/com/qualcomm/robotcore/eventloop/opmode/OpMode.java +++ b/Vision/src/main/java/com/qualcomm/robotcore/eventloop/opmode/OpMode.java @@ -33,10 +33,10 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE import com.qualcomm.robotcore.hardware.Gamepad; import com.qualcomm.robotcore.hardware.HardwareMap; -import io.github.deltacv.vision.external.util.FrameQueue; -import io.github.deltacv.vision.internal.opmode.OpModeNotification; -import io.github.deltacv.vision.internal.opmode.OpModeNotifier; -import io.github.deltacv.vision.internal.opmode.OpModeState; +import org.deltacv.vision.external.util.FrameQueue; +import org.deltacv.vision.internal.opmode.OpModeNotification; +import org.deltacv.vision.internal.opmode.OpModeNotifier; +import org.deltacv.vision.internal.opmode.OpModeState; import org.openftc.easyopencv.TimestampedOpenCvPipeline; import org.firstinspires.ftc.robotcore.external.Telemetry; import org.opencv.core.Mat; diff --git a/Vision/src/main/java/com/qualcomm/robotcore/hardware/HardwareMap.java b/Vision/src/main/java/com/qualcomm/robotcore/hardware/HardwareMap.java index aa1307af..74aba1f5 100644 --- a/Vision/src/main/java/com/qualcomm/robotcore/hardware/HardwareMap.java +++ b/Vision/src/main/java/com/qualcomm/robotcore/hardware/HardwareMap.java @@ -5,8 +5,8 @@ package com.qualcomm.robotcore.hardware; -import io.github.deltacv.vision.external.source.ThreadVisionSourceProvider; -import io.github.deltacv.vision.internal.source.ftc.SourcedCameraNameImpl; +import org.deltacv.vision.external.source.ThreadVisionSourceProvider; +import org.deltacv.vision.internal.source.ftc.SourcedCameraNameImpl; import org.firstinspires.ftc.robotcore.external.hardware.camera.CameraName; public class HardwareMap { diff --git a/Vision/src/main/java/io/github/deltacv/eocvsim/pipeline/StreamableOpenCvPipeline.java b/Vision/src/main/java/org/deltacv/eocvsim/pipeline/StreamableOpenCvPipeline.java similarity index 93% rename from Vision/src/main/java/io/github/deltacv/eocvsim/pipeline/StreamableOpenCvPipeline.java rename to Vision/src/main/java/org/deltacv/eocvsim/pipeline/StreamableOpenCvPipeline.java index 49c9c22c..dc419198 100644 --- a/Vision/src/main/java/io/github/deltacv/eocvsim/pipeline/StreamableOpenCvPipeline.java +++ b/Vision/src/main/java/org/deltacv/eocvsim/pipeline/StreamableOpenCvPipeline.java @@ -3,9 +3,9 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.pipeline; +package org.deltacv.eocvsim.pipeline; -import io.github.deltacv.eocvsim.stream.ImageStreamer; +import org.deltacv.eocvsim.stream.ImageStreamer; import org.opencv.core.Mat; import org.openftc.easyopencv.OpenCvPipeline; import org.slf4j.Logger; diff --git a/Vision/src/main/java/io/github/deltacv/eocvsim/stream/ImageStreamer.kt b/Vision/src/main/java/org/deltacv/eocvsim/stream/ImageStreamer.kt similarity index 82% rename from Vision/src/main/java/io/github/deltacv/eocvsim/stream/ImageStreamer.kt rename to Vision/src/main/java/org/deltacv/eocvsim/stream/ImageStreamer.kt index dfa60b46..5a23ab36 100644 --- a/Vision/src/main/java/io/github/deltacv/eocvsim/stream/ImageStreamer.kt +++ b/Vision/src/main/java/org/deltacv/eocvsim/stream/ImageStreamer.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.eocvsim.stream +package org.deltacv.eocvsim.stream import org.opencv.core.Mat diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/FrameReceiverOpenCvCamera.java b/Vision/src/main/java/org/deltacv/vision/external/external/FrameReceiverOpenCvCamera.java similarity index 93% rename from Vision/src/main/java/io/github/deltacv/vision/external/FrameReceiverOpenCvCamera.java rename to Vision/src/main/java/org/deltacv/vision/external/external/FrameReceiverOpenCvCamera.java index 1110ed82..0e38d92c 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/FrameReceiverOpenCvCamera.java +++ b/Vision/src/main/java/org/deltacv/vision/external/external/FrameReceiverOpenCvCamera.java @@ -3,10 +3,10 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external; +package org.deltacv.vision.external; -import io.github.deltacv.vision.external.source.VisionSource; -import io.github.deltacv.vision.external.source.FrameReceiver; +import org.deltacv.vision.external.source.VisionSource; +import org.deltacv.vision.external.source.FrameReceiver; import org.firstinspires.ftc.robotcore.external.hardware.camera.CameraControls; import org.firstinspires.ftc.robotcore.external.hardware.camera.controls.*; import org.firstinspires.ftc.robotcore.internal.camera.calibration.CameraCalibrationIdentity; diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/PipelineRenderHook.kt b/Vision/src/main/java/org/deltacv/vision/external/external/PipelineRenderHook.kt similarity index 94% rename from Vision/src/main/java/io/github/deltacv/vision/external/PipelineRenderHook.kt rename to Vision/src/main/java/org/deltacv/vision/external/external/PipelineRenderHook.kt index 8c9eecd6..f5ab80b0 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/PipelineRenderHook.kt +++ b/Vision/src/main/java/org/deltacv/vision/external/external/PipelineRenderHook.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external +package org.deltacv.vision.external import android.graphics.Canvas import org.openftc.easyopencv.OpenCvViewport diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/gui/component/ImageX.java b/Vision/src/main/java/org/deltacv/vision/external/gui/component/ImageX.java similarity index 87% rename from Vision/src/main/java/io/github/deltacv/vision/external/gui/component/ImageX.java rename to Vision/src/main/java/org/deltacv/vision/external/gui/component/ImageX.java index b216e769..dbca8153 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/gui/component/ImageX.java +++ b/Vision/src/main/java/org/deltacv/vision/external/gui/component/ImageX.java @@ -3,10 +3,10 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external.gui.component; +package org.deltacv.vision.external.gui.component; -import io.github.deltacv.vision.external.gui.util.ImgUtil; -import io.github.deltacv.vision.external.util.CvUtil; +import org.deltacv.vision.external.gui.util.ImgUtil; +import org.deltacv.vision.external.util.CvUtil; import org.opencv.core.Mat; import javax.swing.*; diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/gui/SkiaPanel.kt b/Vision/src/main/java/org/deltacv/vision/external/gui/gui/SkiaPanel.kt similarity index 91% rename from Vision/src/main/java/io/github/deltacv/vision/external/gui/SkiaPanel.kt rename to Vision/src/main/java/org/deltacv/vision/external/gui/gui/SkiaPanel.kt index 2a61f7f0..e79fd114 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/gui/SkiaPanel.kt +++ b/Vision/src/main/java/org/deltacv/vision/external/gui/gui/SkiaPanel.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external.gui +package org.deltacv.vision.external.gui import org.jetbrains.skiko.ClipComponent import org.jetbrains.skiko.SkiaLayer diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt b/Vision/src/main/java/org/deltacv/vision/external/gui/gui/SwingOpenCvViewport.kt similarity index 99% rename from Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt rename to Vision/src/main/java/org/deltacv/vision/external/gui/gui/SwingOpenCvViewport.kt index 64f098aa..48e3b6cc 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/gui/SwingOpenCvViewport.kt +++ b/Vision/src/main/java/org/deltacv/vision/external/gui/gui/SwingOpenCvViewport.kt @@ -3,11 +3,11 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external.gui +package org.deltacv.vision.external.gui import android.graphics.Bitmap import android.graphics.Canvas -import io.github.deltacv.common.image.MatPoster +import org.deltacv.common.image.MatPoster import org.firstinspires.ftc.robotcore.internal.collections.EvictingBlockingQueue import org.jetbrains.skia.Color import org.jetbrains.skiko.SkiaLayer diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/gui/util/ImgUtil.java b/Vision/src/main/java/org/deltacv/vision/external/gui/util/ImgUtil.java similarity index 89% rename from Vision/src/main/java/io/github/deltacv/vision/external/gui/util/ImgUtil.java rename to Vision/src/main/java/org/deltacv/vision/external/gui/util/ImgUtil.java index 8a4583d7..21053c61 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/gui/util/ImgUtil.java +++ b/Vision/src/main/java/org/deltacv/vision/external/gui/util/ImgUtil.java @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external.gui.util; +package org.deltacv.vision.external.gui.util; import javax.swing.*; import java.awt.*; diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/CameraControlMap.java b/Vision/src/main/java/org/deltacv/vision/external/source/CameraControlMap.java similarity index 82% rename from Vision/src/main/java/io/github/deltacv/vision/external/source/CameraControlMap.java rename to Vision/src/main/java/org/deltacv/vision/external/source/CameraControlMap.java index 9aed0b9d..74e63a1d 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/CameraControlMap.java +++ b/Vision/src/main/java/org/deltacv/vision/external/source/CameraControlMap.java @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external.source; +package org.deltacv.vision.external.source; import org.firstinspires.ftc.robotcore.external.hardware.camera.controls.CameraControl; diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/FrameReceiver.java b/Vision/src/main/java/org/deltacv/vision/external/source/FrameReceiver.java similarity index 80% rename from Vision/src/main/java/io/github/deltacv/vision/external/source/FrameReceiver.java rename to Vision/src/main/java/org/deltacv/vision/external/source/FrameReceiver.java index 368407ab..8df08f2c 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/FrameReceiver.java +++ b/Vision/src/main/java/org/deltacv/vision/external/source/FrameReceiver.java @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external.source; +package org.deltacv.vision.external.source; import org.opencv.core.Mat; diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/ThreadVisionSourceProvider.java b/Vision/src/main/java/org/deltacv/vision/external/source/ThreadVisionSourceProvider.java similarity index 89% rename from Vision/src/main/java/io/github/deltacv/vision/external/source/ThreadVisionSourceProvider.java rename to Vision/src/main/java/org/deltacv/vision/external/source/ThreadVisionSourceProvider.java index 5b238573..20476f9e 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/ThreadVisionSourceProvider.java +++ b/Vision/src/main/java/org/deltacv/vision/external/source/ThreadVisionSourceProvider.java @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external.source; +package org.deltacv.vision.external.source; public final class ThreadVisionSourceProvider { diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/ViewportVisionSourceProvider.java b/Vision/src/main/java/org/deltacv/vision/external/source/ViewportVisionSourceProvider.java similarity index 80% rename from Vision/src/main/java/io/github/deltacv/vision/external/source/ViewportVisionSourceProvider.java rename to Vision/src/main/java/org/deltacv/vision/external/source/ViewportVisionSourceProvider.java index f415da34..77350f10 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/ViewportVisionSourceProvider.java +++ b/Vision/src/main/java/org/deltacv/vision/external/source/ViewportVisionSourceProvider.java @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external.source; +package org.deltacv.vision.external.source; import org.openftc.easyopencv.OpenCvViewport; diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSource.java b/Vision/src/main/java/org/deltacv/vision/external/source/VisionSource.java similarity index 82% rename from Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSource.java rename to Vision/src/main/java/org/deltacv/vision/external/source/VisionSource.java index d09d473b..5ca329a0 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSource.java +++ b/Vision/src/main/java/org/deltacv/vision/external/source/VisionSource.java @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external.source; +package org.deltacv.vision.external.source; import org.opencv.core.Size; diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceBase.java b/Vision/src/main/java/org/deltacv/vision/external/source/VisionSourceBase.java similarity index 95% rename from Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceBase.java rename to Vision/src/main/java/org/deltacv/vision/external/source/VisionSourceBase.java index a13093b2..cae9310c 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceBase.java +++ b/Vision/src/main/java/org/deltacv/vision/external/source/VisionSourceBase.java @@ -3,10 +3,10 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external.source; +package org.deltacv.vision.external.source; -import io.github.deltacv.vision.external.util.ThrowableHandler; -import io.github.deltacv.vision.external.util.Timestamped; +import org.deltacv.vision.external.util.ThrowableHandler; +import org.deltacv.vision.external.util.Timestamped; import org.opencv.core.Mat; import org.opencv.core.Size; import org.slf4j.Logger; diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceProvider.java b/Vision/src/main/java/org/deltacv/vision/external/source/VisionSourceProvider.java similarity index 73% rename from Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceProvider.java rename to Vision/src/main/java/org/deltacv/vision/external/source/VisionSourceProvider.java index 0eee5b7d..cdca635c 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/source/VisionSourceProvider.java +++ b/Vision/src/main/java/org/deltacv/vision/external/source/VisionSourceProvider.java @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external.source; +package org.deltacv.vision.external.source; public interface VisionSourceProvider { diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/util/extension/CvExt.kt b/Vision/src/main/java/org/deltacv/vision/external/util/extension/CvExt.kt similarity index 89% rename from Vision/src/main/java/io/github/deltacv/vision/external/util/extension/CvExt.kt rename to Vision/src/main/java/org/deltacv/vision/external/util/extension/CvExt.kt index 6e3dcd21..4cd39d48 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/util/extension/CvExt.kt +++ b/Vision/src/main/java/org/deltacv/vision/external/util/extension/CvExt.kt @@ -5,7 +5,7 @@ @file:JvmName("CvExt") -package io.github.deltacv.vision.external.util.extension +package org.deltacv.vision.external.util.extension import com.qualcomm.robotcore.util.Range import org.opencv.core.CvType diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/util/CvUtil.java b/Vision/src/main/java/org/deltacv/vision/external/util/util/CvUtil.java similarity index 94% rename from Vision/src/main/java/io/github/deltacv/vision/external/util/CvUtil.java rename to Vision/src/main/java/org/deltacv/vision/external/util/util/CvUtil.java index 74d49050..5e6be89e 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/util/CvUtil.java +++ b/Vision/src/main/java/org/deltacv/vision/external/util/util/CvUtil.java @@ -3,9 +3,9 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external.util; +package org.deltacv.vision.external.util; -import io.github.deltacv.vision.external.util.extension.CvExt; +import org.deltacv.vision.external.util.extension.CvExt; import org.opencv.core.Mat; import org.opencv.core.MatOfByte; import org.opencv.core.Size; diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/util/FrameQueue.java b/Vision/src/main/java/org/deltacv/vision/external/util/util/FrameQueue.java similarity index 92% rename from Vision/src/main/java/io/github/deltacv/vision/external/util/FrameQueue.java rename to Vision/src/main/java/org/deltacv/vision/external/util/util/FrameQueue.java index d42e5287..36631821 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/util/FrameQueue.java +++ b/Vision/src/main/java/org/deltacv/vision/external/util/util/FrameQueue.java @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external.util; +package org.deltacv.vision.external.util; import org.firstinspires.ftc.robotcore.internal.collections.EvictingBlockingQueue; import org.opencv.core.Mat; diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/util/ThrowableHandler.java b/Vision/src/main/java/org/deltacv/vision/external/util/util/ThrowableHandler.java similarity index 73% rename from Vision/src/main/java/io/github/deltacv/vision/external/util/ThrowableHandler.java rename to Vision/src/main/java/org/deltacv/vision/external/util/util/ThrowableHandler.java index 67bae3ab..811f114a 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/util/ThrowableHandler.java +++ b/Vision/src/main/java/org/deltacv/vision/external/util/util/ThrowableHandler.java @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external.util; +package org.deltacv.vision.external.util; public interface ThrowableHandler { void handle(Throwable e); diff --git a/Vision/src/main/java/io/github/deltacv/vision/external/util/Timestamped.java b/Vision/src/main/java/org/deltacv/vision/external/util/util/Timestamped.java similarity index 85% rename from Vision/src/main/java/io/github/deltacv/vision/external/util/Timestamped.java rename to Vision/src/main/java/org/deltacv/vision/external/util/util/Timestamped.java index c35de870..c7f2e174 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/external/util/Timestamped.java +++ b/Vision/src/main/java/org/deltacv/vision/external/util/util/Timestamped.java @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.external.util; +package org.deltacv.vision.external.util; public class Timestamped { diff --git a/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/Enums.kt b/Vision/src/main/java/org/deltacv/vision/internal/opmode/Enums.kt similarity index 79% rename from Vision/src/main/java/io/github/deltacv/vision/internal/opmode/Enums.kt rename to Vision/src/main/java/org/deltacv/vision/internal/opmode/Enums.kt index 8934a750..d21deeff 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/Enums.kt +++ b/Vision/src/main/java/org/deltacv/vision/internal/opmode/Enums.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.internal.opmode +package org.deltacv.vision.internal.opmode enum class OpModeNotification { INIT, START, STOP, NOTHING } enum class OpModeState { SELECTED, INIT, START, STOP, STOPPED } diff --git a/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/OpModeNotifier.kt b/Vision/src/main/java/org/deltacv/vision/internal/opmode/OpModeNotifier.kt similarity index 94% rename from Vision/src/main/java/io/github/deltacv/vision/internal/opmode/OpModeNotifier.kt rename to Vision/src/main/java/org/deltacv/vision/internal/opmode/OpModeNotifier.kt index 782c1e60..cc30b7ea 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/OpModeNotifier.kt +++ b/Vision/src/main/java/org/deltacv/vision/internal/opmode/OpModeNotifier.kt @@ -3,7 +3,7 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.internal.opmode +package org.deltacv.vision.internal.opmode import com.github.serivesmejia.eocvsim.util.event.EventHandler import org.firstinspires.ftc.robotcore.internal.collections.EvictingBlockingQueue diff --git a/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/RedirectToOpModeThrowableHandler.kt b/Vision/src/main/java/org/deltacv/vision/internal/opmode/RedirectToOpModeThrowableHandler.kt similarity index 67% rename from Vision/src/main/java/io/github/deltacv/vision/internal/opmode/RedirectToOpModeThrowableHandler.kt rename to Vision/src/main/java/org/deltacv/vision/internal/opmode/RedirectToOpModeThrowableHandler.kt index 3a00571a..137af6c0 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/internal/opmode/RedirectToOpModeThrowableHandler.kt +++ b/Vision/src/main/java/org/deltacv/vision/internal/opmode/RedirectToOpModeThrowableHandler.kt @@ -3,9 +3,9 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.internal.opmode +package org.deltacv.vision.internal.opmode -import io.github.deltacv.vision.external.util.ThrowableHandler +import org.deltacv.vision.external.util.ThrowableHandler class RedirectToOpModeThrowableHandler(private val notifier: OpModeNotifier) : ThrowableHandler { override fun handle(e: Throwable) { diff --git a/Vision/src/main/java/io/github/deltacv/vision/internal/source/ftc/SourcedCameraName.java b/Vision/src/main/java/org/deltacv/vision/internal/source/ftc/SourcedCameraName.java similarity index 83% rename from Vision/src/main/java/io/github/deltacv/vision/internal/source/ftc/SourcedCameraName.java rename to Vision/src/main/java/org/deltacv/vision/internal/source/ftc/SourcedCameraName.java index 51b2906e..a6216271 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/internal/source/ftc/SourcedCameraName.java +++ b/Vision/src/main/java/org/deltacv/vision/internal/source/ftc/SourcedCameraName.java @@ -3,9 +3,9 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.internal.source.ftc; +package org.deltacv.vision.internal.source.ftc; -import io.github.deltacv.vision.external.source.VisionSource; +import org.deltacv.vision.external.source.VisionSource; import org.firstinspires.ftc.robotcore.external.hardware.camera.CameraCharacteristics; import org.firstinspires.ftc.robotcore.external.hardware.camera.WebcamName; diff --git a/Vision/src/main/java/io/github/deltacv/vision/internal/source/ftc/SourcedCameraNameImpl.java b/Vision/src/main/java/org/deltacv/vision/internal/source/ftc/SourcedCameraNameImpl.java similarity index 87% rename from Vision/src/main/java/io/github/deltacv/vision/internal/source/ftc/SourcedCameraNameImpl.java rename to Vision/src/main/java/org/deltacv/vision/internal/source/ftc/SourcedCameraNameImpl.java index 6ab76332..95970a68 100644 --- a/Vision/src/main/java/io/github/deltacv/vision/internal/source/ftc/SourcedCameraNameImpl.java +++ b/Vision/src/main/java/org/deltacv/vision/internal/source/ftc/SourcedCameraNameImpl.java @@ -3,12 +3,12 @@ * Licensed under the MIT License. */ -package io.github.deltacv.vision.internal.source.ftc; +package org.deltacv.vision.internal.source.ftc; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.qualcomm.robotcore.util.SerialNumber; -import io.github.deltacv.vision.external.source.VisionSource; +import org.deltacv.vision.external.source.VisionSource; import org.jetbrains.annotations.NotNull; public class SourcedCameraNameImpl extends SourcedCameraName { diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortal.java b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortal.java index 513314b5..b4ee308f 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortal.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortal.java @@ -38,8 +38,8 @@ import java.util.ArrayList; import java.util.List; -import io.github.deltacv.vision.external.source.ThreadVisionSourceProvider; -import io.github.deltacv.vision.internal.source.ftc.SourcedCameraNameImpl; +import org.deltacv.vision.external.source.ThreadVisionSourceProvider; +import org.deltacv.vision.internal.source.ftc.SourcedCameraNameImpl; import org.firstinspires.ftc.robotcore.external.hardware.camera.BuiltinCameraDirection; import org.firstinspires.ftc.robotcore.external.hardware.camera.CameraName; import org.firstinspires.ftc.robotcore.external.hardware.camera.WebcamName; diff --git a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortalImpl.java b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortalImpl.java index e751b687..d50be844 100644 --- a/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortalImpl.java +++ b/Vision/src/main/java/org/firstinspires/ftc/vision/VisionPortalImpl.java @@ -38,7 +38,7 @@ import com.qualcomm.robotcore.util.RobotLog; -import io.github.deltacv.vision.internal.source.ftc.SourcedCameraName; +import org.deltacv.vision.internal.source.ftc.SourcedCameraName; import org.firstinspires.ftc.robotcore.external.hardware.camera.CameraName; import org.firstinspires.ftc.robotcore.external.hardware.camera.WebcamName; import org.firstinspires.ftc.robotcore.external.hardware.camera.controls.CameraControl; diff --git a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraBase.java b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraBase.java index b080e5aa..0f89c0f0 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraBase.java +++ b/Vision/src/main/java/org/openftc/easyopencv/OpenCvCameraBase.java @@ -21,8 +21,8 @@ package org.openftc.easyopencv; -import io.github.deltacv.common.pipeline.util.PipelineStatisticsCalculator; -import io.github.deltacv.vision.external.PipelineRenderHook; +import org.deltacv.common.pipeline.PipelineStatisticsCalculator; +import org.deltacv.vision.external.PipelineRenderHook; import org.opencv.core.*; import org.opencv.imgproc.Imgproc; diff --git a/Vision/src/main/java/org/openftc/easyopencv/SourcedOpenCvCameraFactoryImpl.java b/Vision/src/main/java/org/openftc/easyopencv/SourcedOpenCvCameraFactoryImpl.java index 4a6e41db..0584ee6a 100644 --- a/Vision/src/main/java/org/openftc/easyopencv/SourcedOpenCvCameraFactoryImpl.java +++ b/Vision/src/main/java/org/openftc/easyopencv/SourcedOpenCvCameraFactoryImpl.java @@ -5,11 +5,11 @@ package org.openftc.easyopencv; -import io.github.deltacv.vision.external.FrameReceiverOpenCvCamera; -import io.github.deltacv.vision.external.source.VisionSource; -import io.github.deltacv.vision.external.source.ThreadVisionSourceProvider; -import io.github.deltacv.vision.external.source.ViewportVisionSourceProvider; -import io.github.deltacv.vision.internal.source.ftc.SourcedCameraName; +import org.deltacv.vision.external.FrameReceiverOpenCvCamera; +import org.deltacv.vision.external.source.VisionSource; +import org.deltacv.vision.external.source.ThreadVisionSourceProvider; +import org.deltacv.vision.external.source.ViewportVisionSourceProvider; +import org.deltacv.vision.internal.source.ftc.SourcedCameraName; import org.firstinspires.ftc.robotcore.external.hardware.camera.WebcamName; public class SourcedOpenCvCameraFactoryImpl extends OpenCvCameraFactory { From 5b30084ec896933bd23a1bd1693aaf8698090d3c Mon Sep 17 00:00:00 2001 From: Sebastian Erives Date: Tue, 19 May 2026 11:28:12 -0600 Subject: [PATCH 39/40] Use jackson for TOML, improve camera creation dialog & update gradle workspace with proper libraries --- .../common/util/{ => serialization}/Toml.java | 2 +- .../eocvsim/plugin/loader/PluginLoader.kt | 2 +- .../eocvsim/plugin/security/Authority.kt | 2 +- .../security/PluginSignatureVerifier.kt | 2 +- .../serivesmejia/eocvsim/Bootstrap.java | 2 +- .../gui/dialog/source/CreateCameraSource.kt | 179 +++++++++++++----- .../gui/dialog/source/CreateHttpSource.kt | 12 +- .../gui/dialog/source/CreateImageSource.kt | 12 +- .../gui/dialog/source/CreateVideoSource.kt | 12 +- .../util/template/DefaultWorkspaceTemplate.kt | 10 +- .../util/template/GradleWorkspaceTemplate.kt | 2 - .../plugin/loader/FilePluginLoaderImpl.kt | 2 +- .../repository/PluginRepositoryManager.kt | 2 +- .../security/superaccess/SuperAccessDaemon.kt | 2 +- .../resources/templates/gradle_workspace.zip | Bin 98915 -> 99788 bytes 15 files changed, 166 insertions(+), 77 deletions(-) rename Common/src/main/java/org/deltacv/common/util/{ => serialization}/Toml.java (95%) diff --git a/Common/src/main/java/org/deltacv/common/util/Toml.java b/Common/src/main/java/org/deltacv/common/util/serialization/Toml.java similarity index 95% rename from Common/src/main/java/org/deltacv/common/util/Toml.java rename to Common/src/main/java/org/deltacv/common/util/serialization/Toml.java index 0b5dc860..4c78a4cc 100644 --- a/Common/src/main/java/org/deltacv/common/util/Toml.java +++ b/Common/src/main/java/org/deltacv/common/util/serialization/Toml.java @@ -1,4 +1,4 @@ -package org.deltacv.common.util; +package org.deltacv.common.util.serialization; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; diff --git a/Common/src/main/java/org/deltacv/eocvsim/plugin/loader/PluginLoader.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/loader/PluginLoader.kt index 2b44bb08..3573f412 100644 --- a/Common/src/main/java/org/deltacv/eocvsim/plugin/loader/PluginLoader.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/loader/PluginLoader.kt @@ -6,7 +6,7 @@ package org.deltacv.eocvsim.plugin.loader import com.github.serivesmejia.eocvsim.util.extension.hashString -import org.deltacv.common.util.Toml +import org.deltacv.common.util.serialization.Toml import org.deltacv.eocvsim.plugin.EOCVSimPlugin import org.deltacv.eocvsim.plugin.api.EOCVSimApi import org.deltacv.eocvsim.plugin.security.PluginSignature diff --git a/Common/src/main/java/org/deltacv/eocvsim/plugin/security/Authority.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/security/Authority.kt index e62fbb45..4dedf074 100644 --- a/Common/src/main/java/org/deltacv/eocvsim/plugin/security/Authority.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/security/Authority.kt @@ -8,7 +8,7 @@ package org.deltacv.eocvsim.plugin.security import com.github.serivesmejia.eocvsim.util.extension.plus import com.github.serivesmejia.eocvsim.util.io.LockFile import org.deltacv.common.util.loggerForThis -import org.deltacv.common.util.Toml +import org.deltacv.common.util.serialization.Toml import org.deltacv.eocvsim.plugin.PLUGIN_CACHING_FOLDER import java.io.File import java.net.URI diff --git a/Common/src/main/java/org/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt b/Common/src/main/java/org/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt index 570e0faf..d5de8386 100644 --- a/Common/src/main/java/org/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt +++ b/Common/src/main/java/org/deltacv/eocvsim/plugin/security/PluginSignatureVerifier.kt @@ -7,7 +7,7 @@ package org.deltacv.eocvsim.plugin.security import com.github.serivesmejia.eocvsim.util.extension.hashString import org.deltacv.common.util.loggerForThis -import org.deltacv.common.util.Toml +import org.deltacv.common.util.serialization.Toml import org.deltacv.eocvsim.plugin.loader.InvalidPluginException import java.io.File import java.security.PublicKey diff --git a/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java b/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java index 2174ddd3..94db21fb 100644 --- a/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java +++ b/EOCV-Sim/src/bootstrap/java/com/github/serivesmejia/eocvsim/Bootstrap.java @@ -409,6 +409,6 @@ private static boolean isJava25OrNewer(File home) { // ---------------- LOG ---------------- private static void log(String msg) { - System.out.println("[EOCV-SIM BOOTSTRAP] " + msg); + System.out.println("[EOCV-Sim/Bootstrap] " + msg); } } \ No newline at end of file diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt index 452301f2..25c0e25b 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateCameraSource.kt @@ -25,7 +25,9 @@ class CreateCameraSource : KoinComponent { private val visualizer: Visualizer by inject() private val camerasComboBox = JComboBox() - private val modesComboBox = JComboBox() + private val resolutionComboBox = JComboBox() + private val fpsComboBox = JComboBox() + private val pixelFormatComboBox = JComboBox() private val nameTextField = JTextField(20) private val sameCameraCheckBox = JCheckBox("Match to exact port") @@ -78,38 +80,13 @@ class CreateCameraSource : KoinComponent { loadModes(camerasComboBox.selectedIndex) } - // ---------------- MODE RENDERER ---------------- - - modesComboBox.renderer = object : DefaultListCellRenderer() { - - override fun getListCellRendererComponent( - list: JList<*>, - value: Any?, - index: Int, - isSelected: Boolean, - cellHasFocus: Boolean - ): Component { - - super.getListCellRendererComponent( - list, - value, - index, - isSelected, - cellHasFocus - ) - - val mode = value as? VideoMode - - text = if (mode != null) { - buildString { - append("${mode.width}x${mode.height}") - append(" @ ${mode.fps}fps") - append(" (${mode.pixelFormat})") - } - } else { - "Unknown" - } + // ---------------- MODE CONTROLS ---------------- + // simple renderers (defaults are fine, but keep fps/pixfmt readable) + fpsComboBox.renderer = object : DefaultListCellRenderer() { + override fun getListCellRendererComponent(list: JList<*>, value: Any?, index: Int, isSelected: Boolean, cellHasFocus: Boolean): Component { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus) + text = (value as? Int)?.toString() ?: "-" return this } } @@ -125,26 +102,48 @@ class CreateCameraSource : KoinComponent { gbc.weightx = 1.0 root.add(camerasComboBox, gbc) + // Resolution gbc.gridx = 0 gbc.gridy = 1 gbc.weightx = 0.0 - root.add(JLabel("Mode:"), gbc) + root.add(JLabel("Resolution:"), gbc) gbc.gridx = 1 gbc.weightx = 1.0 - root.add(modesComboBox, gbc) + root.add(resolutionComboBox, gbc) + // FPS gbc.gridx = 0 gbc.gridy = 2 gbc.weightx = 0.0 - root.add(JLabel("Name:"), gbc) + root.add(JLabel("FPS:"), gbc) + + gbc.gridx = 1 + gbc.weightx = 1.0 + root.add(fpsComboBox, gbc) + + // Pixel Format + gbc.gridx = 0 + gbc.gridy = 3 + gbc.weightx = 0.0 + root.add(JLabel("Pixel Format:"), gbc) + + gbc.gridx = 1 + gbc.weightx = 1.0 + root.add(pixelFormatComboBox, gbc) + + // Name + gbc.gridx = 0 + gbc.gridy = 4 + gbc.weightx = 0.0 + root.add(JLabel("Source Name:"), gbc) gbc.gridx = 1 gbc.weightx = 1.0 root.add(nameTextField, gbc) gbc.gridx = 1 - gbc.gridy = 3 + gbc.gridy = 5 gbc.weightx = 1.0 root.add(sameCameraCheckBox, gbc) @@ -158,7 +157,7 @@ class CreateCameraSource : KoinComponent { buttons.add(cancelButton) gbc.gridx = 0 - gbc.gridy = 4 + gbc.gridy = 6 gbc.gridwidth = 2 root.add(buttons, gbc) @@ -219,15 +218,86 @@ class CreateCameraSource : KoinComponent { modes = emptyArray() } - modesComboBox.removeAllItems() + // Populate resolution / fps / pixel format combo boxes based on available modes + resolutionComboBox.removeAllItems() + fpsComboBox.removeAllItems() + pixelFormatComboBox.removeAllItems() + + val resolutions = modes + .map { "${it.width}x${it.height}" } + .distinct() + .sortedByDescending { res -> + val (w, h) = parseResolution(res) ?: Pair(0, 0) + w * h + } + + for (r in resolutions) resolutionComboBox.addItem(r) + + if (resolutions.isNotEmpty()) { + resolutionComboBox.selectedIndex = 0 + } + + // listeners (remove previous to avoid duplicates) + for (l in resolutionComboBox.actionListeners) resolutionComboBox.removeActionListener(l) + for (l in fpsComboBox.actionListeners) fpsComboBox.removeActionListener(l) + + resolutionComboBox.addActionListener { + updateFpsAndPixelFormats() + } + + fpsComboBox.addActionListener { + updatePixelFormatsForFps() + } + + // initialize dependent lists + if (resolutionComboBox.itemCount > 0) updateFpsAndPixelFormats() + } - for (mode in modes) { - modesComboBox.addItem(mode) + private fun parseResolution(res: String?): Pair? { + if (res == null) return null + val parts = res.split('x') + if (parts.size != 2) return null + return try { + Pair(parts[0].toInt(), parts[1].toInt()) + } catch (_: NumberFormatException) { + null } + } + + private fun updateFpsAndPixelFormats() { + val res = resolutionComboBox.selectedItem as? String ?: return + val (w, h) = parseResolution(res) ?: return + + val modesForRes = modes.filter { it.width == w && it.height == h } + + val fpsList = modesForRes.map { it.fps }.distinct().sortedDescending() + + fpsComboBox.removeAllItems() + for (f in fpsList) fpsComboBox.addItem(f) + if (fpsComboBox.itemCount > 0) fpsComboBox.selectedIndex = 0 + + // populate pixel formats for the initially selected fps + updatePixelFormatsForFps() + } - if (modes.isNotEmpty()) { - modesComboBox.selectedIndex = 0 + private fun updatePixelFormatsForFps() { + val res = resolutionComboBox.selectedItem as? String ?: return + val (w, h) = parseResolution(res) ?: return + val selectedFps = fpsComboBox.selectedItem as? Int + + val modesForRes = modes.filter { it.width == w && it.height == h } + + val pfList = if (selectedFps != null) { + modesForRes.filter { it.fps == selectedFps }.map { it.pixelFormat } + } else { + modesForRes.map { it.pixelFormat } } + + val distinctPf = pfList.distinct() + + pixelFormatComboBox.removeAllItems() + for (pf in distinctPf) pixelFormatComboBox.addItem(pf) + if (pixelFormatComboBox.itemCount > 0) pixelFormatComboBox.selectedIndex = 0 } private fun create() { @@ -237,8 +307,29 @@ class CreateCameraSource : KoinComponent { val info = cameraInfos.getOrNull(camIndex) ?: return - val mode = modes.getOrNull(modesComboBox.selectedIndex) - ?: return + // Build desired VideoMode from selected resolution / fps / pixel format + val resStr = resolutionComboBox.selectedItem as? String ?: return + val (w, h) = parseResolution(resStr) ?: return + val fps = fpsComboBox.selectedItem as? Int + val pf = pixelFormatComboBox.selectedItem + + var mode: VideoMode? = null + + if (fps != null && pf != null) { + mode = modes.find { it.width == w && it.height == h && it.fps == fps && it.pixelFormat == pf } + } + + // fallback: match by resolution + fps + if (mode == null && fps != null) { + mode = modes.find { it.width == w && it.height == h && it.fps == fps } + } + + // fallback: any mode with resolution + if (mode == null) { + mode = modes.find { it.width == w && it.height == h } + } + + mode ?: return onMainUpdate.once { diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateHttpSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateHttpSource.kt index ccb7b58c..18bd0111 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateHttpSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateHttpSource.kt @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog.source import com.github.serivesmejia.eocvsim.EOCVSim @@ -53,7 +53,7 @@ class CreateHttpSource : KoinComponent { // Name field gbc.gridy = 1 gbc.gridx = 0 - add(JLabel("Source name: "), gbc) + add(JLabel("Source Name: "), gbc) gbc.gridx = 1 val sourceCount = inputSourceManager.sources.size + 1 nameTextField = JTextField("HttpSource-$sourceCount", 15) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt index d3fbd06d..0111b77d 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateImageSource.kt @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog.source import com.github.serivesmejia.eocvsim.EOCVSim @@ -73,7 +73,7 @@ class CreateImageSource( // Name part val namePanel = JPanel(FlowLayout()) - val nameLabel = JLabel("Source name: ").apply { horizontalAlignment = JLabel.LEFT } + val nameLabel = JLabel("Source Name: ").apply { horizontalAlignment = JLabel.LEFT } nameTextField.text = "ImageSource-${inputSourceManager.sources.size + 1}" namePanel.add(nameLabel) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt index b3aed7dd..9566e4af 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/dialog/source/CreateVideoSource.kt @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.gui.dialog.source import com.github.serivesmejia.eocvsim.EOCVSim @@ -73,7 +73,7 @@ class CreateVideoSource( val namePanel = JPanel(FlowLayout()).apply { val sourceCount = inputSourceManager.sources.size + 1 nameTextField = JTextField("VideoSource-$sourceCount", 15) - add(JLabel("Source name: ")) + add(JLabel("Source Name: ")) add(nameTextField) } contentsPanel.add(namePanel) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/DefaultWorkspaceTemplate.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/DefaultWorkspaceTemplate.kt index de28d0cf..e87a6112 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/DefaultWorkspaceTemplate.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/DefaultWorkspaceTemplate.kt @@ -1,8 +1,8 @@ -/* - * Copyright (c) 2021 Sebastian Erives - * Licensed under the MIT License. - */ - +/* + * Copyright (c) 2021 Sebastian Erives + * Licensed under the MIT License. + */ + package com.github.serivesmejia.eocvsim.workspace.util.template import com.github.serivesmejia.eocvsim.util.SysUtil diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/GradleWorkspaceTemplate.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/GradleWorkspaceTemplate.kt index cfa5ab3d..df7d9f64 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/GradleWorkspaceTemplate.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/workspace/util/template/GradleWorkspaceTemplate.kt @@ -15,8 +15,6 @@ import java.io.IOException object GradleWorkspaceTemplate : WorkspaceTemplate() { - private val TAG = "GradleWorkspaceTemplate" - val logger by loggerForThis() val templateZipResource = javaClass.getResourceAsStream("/templates/gradle_workspace.zip") diff --git a/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt index 12862e44..b674186e 100644 --- a/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/loader/FilePluginLoaderImpl.kt @@ -13,7 +13,7 @@ import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.util.extension.hashString import com.github.serivesmejia.eocvsim.util.extension.plus import org.deltacv.common.util.loggerForThis -import org.deltacv.common.util.Toml +import org.deltacv.common.util.serialization.Toml import org.deltacv.common.util.ParsedVersion import org.deltacv.eocvsim.plugin.EMBEDDED_PLUGIN_FOLDER import org.deltacv.eocvsim.plugin.EOCVSimPlugin diff --git a/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt index 55b617c8..8ba79157 100644 --- a/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt @@ -11,7 +11,7 @@ import com.github.serivesmejia.eocvsim.util.SysUtil import com.github.serivesmejia.eocvsim.util.event.EventHandler import com.github.serivesmejia.eocvsim.util.extension.hashString import com.github.serivesmejia.eocvsim.util.extension.plus -import org.deltacv.common.util.Toml +import org.deltacv.common.util.serialization.Toml import org.deltacv.common.util.ParsedVersion import org.deltacv.common.util.loggerForThis import org.deltacv.eocvsim.plugin.loader.PluginManager diff --git a/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemon.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemon.kt index 71ba4410..7f937246 100644 --- a/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemon.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/security/superaccess/SuperAccessDaemon.kt @@ -10,7 +10,7 @@ import com.github.serivesmejia.eocvsim.util.extension.plus import com.github.serivesmejia.eocvsim.util.serialization.JacksonJsonSupport import org.deltacv.common.util.loggerForThis import com.fasterxml.jackson.annotation.JsonTypeInfo -import org.deltacv.common.util.Toml +import org.deltacv.common.util.serialization.Toml import org.deltacv.eocvsim.gui.dialog.SuperAccessRequest import org.deltacv.eocvsim.plugin.loader.PluginInfo import org.deltacv.eocvsim.plugin.loader.PluginManager diff --git a/EOCV-Sim/src/main/resources/templates/gradle_workspace.zip b/EOCV-Sim/src/main/resources/templates/gradle_workspace.zip index bcc1f1ea1b9c6c5f003316813a1c83c17922b7af..002079635cdb4a6e2e2ae767ac4d2c8e9a6aaab4 100644 GIT binary patch delta 78743 zcma&N1CS@d*Dct#t!dkuwr$(C?O#vZwvB1qwr$(CcmDgu+xOna7rQU3vTjsV#i`6& zSvT%Em3a^ZF&PQ*OF5asw(h6 zkd*$32G^NzR{ybyO;GB}kWjE6s3NNWZc6=E|0n2h)&C9(2}B5F?qq0UW9t6j;Rc!g z7w&&-;`AB@qf|vAnszvyC+V78i4}2L5Gg6z7*dhx)Z4eWJ8%N=*Sy|_%Ne9PZdVuV1r%zfOQIM@5p>e!ghGdXtaGRTJpfFsMNl z@}n4>pKY{PA@8E&7H%#A{fNUB4ubZVV-0seAJTfDUn%=F{QIgmWYsst>+FU5vp`;h z1Fj?H5Ws{VhiWXopQI)_s0@n{Hd>Gxr8-Lza|V4U-}0)Yj9I|ry^)1EE6gTR~iiV*M5D^$6VOgZ1i1i6)Cdx zCism;IG7eDroJ5HiEb7)4ElRZ7&x|%vbe&=3c$ZpaP^Gn!pLLZq{%m=z&9Et+KH77 zgU<_5{qK_!gPleNc>d3g0fYaN15qHLm&qevboNQr`JATKmmxDN!%$~EYhLuAwgwAN zu&d6xCwR=w*u7r40+)$1PzM6jf~vGmJG5ojV}BW1X@|cA7LIn*Z`gsZY9y#e#MJ40 z48XA~9iKUnmGCH|rH2J&z1V*4mSKZa)k)Q$)7YiKHg`d8bI$0Ytzmf7{X4R^dvtlJ z@oz*~%2kWRa$dk0a#VUXcFwi~88hz7oS{o%4rET+-J<;N*Aiz?{2#`UNDB)gN2br# z8X;M!8*TL}hQP?XFa<=40kxA#>vyQ&Fo3a5a`J9kw|?MQ$kJK2wQf#yPhOPXVkOJR z!Tt4A1sSLnmDSQ%rL?J$)7Z@+vo1)_(L6=MTH!9)WOfUmpXbUSOxapQ3liaUoa}m#_i5GeROt(hSbftBVU?2)iK2r(QTq5y zRScAfj+7-n>u52|syoYul!K*oYXB5i1r|$CSVMKTE3Uq$O*|+!F%dQVq-)QJcL`|= zsmoC+j@e-ec=a?c1_VO_2zhWu2+5UMSw55oiKBXoL-%mMBJ2r`-JL*&pZ4s>Q)kTW zl}}#M!ntqJ6mq*{)Mcy>Mmu)f$(i6LG`lQR;@?@Jj;0x8*O|+YG|n6?LBP?d7+!fH z$9&{ezQY{q2q9s^wF3thzF>Zw5_K?~7mBea1QroWAwElMX3!muW;;?-C9FQucH^f| zQqtzV#yM-O<}YjX$&$XRr~v~ikSeb+RCRSywQHKcJdH)&YT>(k-8JSoLvt|ZXfdl< zcy9#1m3ju99A2M~fH!ye0RR>1yiuQi1tg(tv_)8zHp2+2gOGWilCynAulg!&u zrTaE6duchnNiW|JRHj{=`UlPGKKCVmh>ls}TmkzS73Ssb+#qV&7GQODvN)Ql5^2oH z->=OV*rZ-ncvOxwk>YYOtN^Wd9S4L{Cg-k$>NMtF7k^DDd_UByr<7RlM{w$>p};OM z%AL{-$_J677x^Vt^sk0 zt9X(fT(}tg#0B)XVWmq{GX#xk-@{xfG9TFOQ9wS*ok{+T zGaV>{j-}oZVgKl0h(8CsOTKL12iPSQxan&_rz%dxrne%GtRvkORCD&1_BgkosS16< zQf#Ko^Jjukh*TM~dvZ$AA6gby@~|5WVD|DyUSEF7$TfypDnRhaL(-_o2iO_3Z&WP2 z7x0!8Lb_3v2?tgbch{c9YP+4B`6+MhOfN9*-tiu{BQWIZ@Lp$jB}ov-y;)!WaarND zXrgJWFeoDdlARUVQ>Il%Cj$t%{j!2MJf>ZNFijV`ukM|au>1R=9PWr`TetH z%NKAiO9_%FlN-aW>#sjo{h$L(I3-K2p)+)!HVsst9RPyvN2wufE^KMY0ik8k;iXLU z%8z?fCbw5YQ=S(1?atfBm5AO%uh9CSUMKKL$y>a?J~s z_2GKI4dqLIrSno-qkO|bE>L&72FI{C8NYqU$k8|3Qd>T7Q$jqcS>-01BNEJH3=4{0 zmI8>(Z~&p`Z|VS%ZC1a(v_4-1bga?p=GrnKHePXAA1q^-QPehNUg`-IP;hyy3-YpVr0ycPtXTRIeJ_PEb=S( zeg>m@Y$cl2PK>Kpa#%CCnri_$8;jgSkf?_sq%yfQ#g-vh1*2cA>Pm4v)>EOW)(hMx z8Gt3{8ODhMapN~&alQI)Xa#9VsJr4O;|C}ppb(7zuj~l+e`ZJYMusl`e;Lx0nzsE0 z8=4b+vpfxwC6ON7d?Jj`Ws*-dXkGnQY`VR;M-5i^C_@cykjQ;zEyGOi@`_@1R z-y|Z2WcGx6w{t!{DP3$V{Vf5#Xj8IH+q{F{^RxOa4GhRjR3eB(mAcUWc8$tmQs;S` zX%bC_@RT!km6c9B9KrV#G>=kw+O7kBcAqr~(yMx3=1x~~Rari%_AWq3xQa5v5C)?% zPe7kp%hCXkSy2s~gr~69(92ZUQy3q`=hkdc2X&FKOnJ|)jsaGL0L%bDQ$@3M<-(RJ zD?e2mPlvpam2(IQA&J04Fd0Phr!rjxVF`3|;1ZYP!7U$nHuY)< zBY$Ut5?c~T8hi&vKT`N@z=aj-;0}Iw3i`=}^(6049Df?otujCO#~__3{TRNGsu)rL zqaRoqeQGtp6pKWE*+kNiYFWhz4xXA?x!k`}Mlz7h3Eh%neg5s_jC zA3R!ng_d$1bNxGhGAYTaxX01LfEB!{K-AI4L=v~uaP|Tm|H6h9UO>_I3LCLCStc!DSX7ENz#egHmY^k&{^NJc*dy9T zLnZaliA-jDZ!Vg6Qq;nN#%4~GRsB`w%kG8M|JISa{fHYqMGJ63ti9jjHNa|1pMnT5jD+H{3)NDge3zIo?Tunoz14y=_JE3G

V7Z68{sty?A@XD5tE9)urnU&{?1Ra3s0=E}FNO|6+R= zDVlpeMm?E~y*MZYZ62t}WPZ$gd(tZmT`l(QJRv#x7gZw?6Sy?4T7@%9HUB*15p#G2 z)&hcK8J?4H%Mcb5H@4De;%?2w6}{2PL*jmTj$XX6OMMr{$l9f%V3iNq=%#|_G5isE zJ@IACUq$0$LN)B!#(J+XVT{t+fY}G^adf30mXeh-eWY$XjQ1+F`YI=7w}dIUk-q!- z8yAQj8puCG^bZlZML`iLKQ1SyQ1V+0KLHETA*{{pT9}#ck}e^y!zI{zV-|Dz!Oi+|3hE-sdK=Fas0MHBzK7?}Q#dH#oe5~6;a=*mvP^fSVB zBR(St2Avybhf(wa63zSA<7$W2)_#*!p<;J;WZPl#gGlmq6W9|(J5so;Du~nf$C<|w z9&)CQ8c|Meh9P)6jgX=>Ry+|Pg7ORov=5yQbklyZ^PGy+HWz7T)NiD~Z1d}DS>D?u zO#Lo_NG@uiVcs|oF`iCt8&W4iTYV-K9ind%8;}!x7v?em)Xi~sY0Av?e5$Z7oV?tuE|4th6dV|x=*hX2kF zMX7LB0$FfY|FQp*a@$JO{_mU{{eOY^f4pnuY;X79Va8Y`8X%>^S^dW*B13WmAfYgd z)TKH9HBaPU{jbOTca8m@%>Mtdt^Xa!e+IyRy>|w8CqoAZQzyRvL$v=W7(@Rrv4IGP zFg4BBTibu{3g#Nhj{qyMo{9iVZPvWaY5&9)o=*{?W_|6@50(pj2q4|0C4UqmlE zey+E*=diQa;?Z^GUDV9fRGR}>8KKbr$s+&8wLf9+kEH<6&m7@TLSlAR7#*Lk^n)Y4ucl1Za)t%YJfzkE7ryWofN1x&b1Oj9L0tz(y^P(Gcd8M1B z`wdeclHxW_A0kIzB1anKGRzh-J=p?^*%^6N9aY>HTaYsb@}7{g3~IBN_ZJ$( zg;lB=xPmz~2s<%tA2KeNa@7V5zN5DWZ2*2W4ym0!YWj7WW#ShDOT#<&HxPb2FA(CD z>>fbZf5p4YvHtazk2cSuEk#s&8dyuU+ohwG0$t)NZCP|<&aktjf&6i}IE(^$frV_M zgr{E5?QIsgg(@o1<2V|PQrI}gjUa+Mk#_vyaB+XIMD#RgMZvg(S}n2~R;R?)lO7Gk z1X&{s__q~b&}SkF#lt)&ItyvykokW5F&)7C`hp#8a>5G2{2h%7D{F~*XI}y-+rGji znOK31%Ov!O93FF2k~}AX23<~&N8TN7V33Wd1s?6mC&e62a#)gatdr;B`@UEnzEx~j zS3rI7(glWJG`5*tsA%o$g1ero_CccY>6#>)&0EkWg{Q1WOGY+pl4d5g^h9=Wv;|-Y z3lDVt>mr-279$Yy`{SkN*9bn|s}Vp>G!t~|)2}vkVVcw38%<*Gm(wOE(8=j>a@tJt z4bh1<4m)^sO?lP*q&&Ll)e%%u*K~7GM5i83fiiVym6%INt5rXn+J|8m5c-oC#O04A zc#1!(>T4bl5GjCQ?)#@D0I=r29s*#l=Nd+=e$e8Tsp+5$6d%HBxVoXc90l|`J97T91vWI3Z&o-oSLE(gr_k+`Q86)C#^JZddWFGSLR zAnvSS9@G@5OELkOkarO&NVx1hnm{NSS7wfB_n|=;3IAqFV@1tYv@bdESOnBvTY&vL z16Cce^C994UPpr55_BLXBQJ0hGRAXL%3XruB|s6h5FT0@DSu$^Id zD898Jabqpz&`nyUiV87V@>z_t`0);7sS%f`jic_jNshwEx@nqFbF#CfyA^e#h}kJY zi_yggW+ zSjb=Kc~CPYsceUg}l!?)=nLkFC6fD81Z7ItG&H*=4f#)}xxtQK`jG=$= zF`tE=5U_U77Y*t}%6ozEtpcwv!NImSzo@%J;2YN95eIw7@G}Zaj64o29$Gr>m;8C# z8z+$$h~wp!bZae=C?=*f@w>da)Ejd7V^;Mrlxq-PO=Y<0GHXfZPc0PLGFp;lrA#h6&qHzY}_54syDk%!F@eb zyr0C}p{vkuY>livg*@9RNO<8|pChJ2u@Ae>xnjWodNcLgPy0wU2M$ z=p=D+I`GPur5&?;JP`fAG#%H7H~o26y}la6Gz>8rbxF`9=v6#{B-4gS zh2w}(=EkIv65MYuIJ*0mfA8h`xF70eFJ%(!`rCM?Acg^JKm)i7OpM6xuwR_YVk9_8 zU{o5O{2J<%mnqmCIbPp5T()04cD*??VX6eyQGh*P-7KRT zcz}JtRmFahv7g}u#*pt5v;ptld(44rN}g5-i4mp4VVQ~ot@aRXk{}-oh$8e5xh;wd z7a$;A`JN-&Uyy7+6{MHM80g-gCooFTK_<=S*=aNvIP-F+l_vD_Gu6X7|Mg1yHC6jn zdhn;K6ur*9rJ;_U_02u%DLN$>$3y^XtDEW=o@qyb+m`3=m5DDr#&Jmt!#fP zCT4{JPq3{gz!MI3=cB-#C1f*}432GF03cv=eU()gULDN(x9g7~&AK#8L8QQNwuvXO zF51RwM~+Zgi5N+Z(Axy zpNvEGFGqt`S5{kDS!icmC?DqB6Pk^?X zLp{4Yv#VQ5or@b&(}%l1mExAtu$t)GTD@Ul4v2C>o`l`k(l`vUYWcE`jxnx*pdml3bN)K@$u8V+he#w;}m}4zc2#(rptFo0C{} za!Ul}Qgdm@yj7cpqr|h82t=|xy=ADn0nJVyeXVN`&ns%xfnlQ1w2Zq|3h3+i_H;sX zM&t#{%*QupsL@Q#JZJ&chji)H*fkA72J56a`}T6mtTqP5dXY>6j|qh#eM3{%|DJg#Ndy6Q{njf8zsp{icJU?do$0f#j`Om>54P^=XtUW;l; zCj#KDK-6v6x|s@>0_55xyop!eI#m^^TfO$NS_~VLHphKO)`cFawX4$|{u-bLM_ga$ zztm9wDhR{K6vJC|0w@Kd@C-ab+1qZI1gsA`Q!~8>IUUAow^=ztkLztg^hamY4mN08 zr;N(^T#_-#RvOg{VW0H})ysY3_2F@H2D5paRn@Rc{N$k8vJ7I2jg0f)#FcatHx=7RAtWEO3*U0ZG=}d8DJuGm5`R|5~#hGi3ZS=%XD2*ZNBNtRyLbg% zRfd)!8O0x$<3$^rMk*WYsVo7>N>r=^&n|HbQSs3k*tnn5`GoQb_30-~+ak>;M3Dh+ ziQ6a|M^m)u51<37p|W`@wK}%cV}PTpCgXGNK)8U)a&@BPJ@{0TpLJh9!n$NyMbzTa zNi57`fa93cl;9VsU*oteiDP_~5iN_d(zapx!L5&bJO2f9VJ7Z~sbNxGe~56Xv#%}o zNCS59hs!Ot>rOmjQyy<}2WYTIhzz4mIEu1HWw4293vjgU#QCK(Da`^dn$Atq##+Z5 zHf4CcQtyAlQ@NkyYGAzooE>r~deeELpfb0{<-TO4e8|L;iP63P`Qp}hpVkyiBEOb3 zTN{I^kN3vR-$lRE>U8S3!_T|mQ@TkuwG%AIkM)n+|Ad;qUqtZmfa~hg%3SHtM#Jk~ z{v1Uq0lv9NNJWzfbdewV3y@*P5eF-KsbL6UX~&}yg^FMzQ%i_iGXvYT%B8=Ljxqf? z*UZ6-AGb?y)r!qC%hHJVnGCmCIw`JM^f_fIyc~A?atR@j9t-fD4};qtJz~!Xd!WAk z!+WtLA18=Gx&)dDo;K#Uf?C9}10wetW9eu>063_+xEEl;1Kuh^Ak?7HePuZ6DQ5V+ zdFNMz;CC2_g&}Ss>PlShgS9ahgFl*#j%Wb5>1F94*|t9z7w2WaGX>0jE88+gugjV! zr=n7~n8VnO<4w`(4=0s^4aCx76%84G2Q3b>oj%iC-J6r`BT^S!%$<0a$qLQ-a3;GR0Rg=Z=Fi90s_?zIG z0`4 zhKY}m=@Eh`vibbssA9uL1#F>SfKZ)_0MyQnhXsQY#7>x1VEC5Q$aZBEc`Q&tA(r4a zFtDdF4$qgDqHP<1?JcczDo@+?`C(~{J_xK7~4 zHBHS`Q^BmwyDPqiH zF%u8H-S(7ekVK-o^@WP_b`12Pr+t*Y4HWUbI%06rWNQVAI& zTqE_K)!#Qjf{v}J>Etcer;uH!#VN1et@>Cc7-v9;)9DW`r6BeU1&cqq4Y6Pb{Wn1P zKmvjP)P-9>uI~@il2s!iZ|#WA*S-Kw(JvnGcnSGHSx3d)6BJ!kWm^;&Nz!)F{Y0wY zs7oKHfr8P`VnGT{97Z+60Fr9TO`FkQ4boTj-+Fk^Ye-`SxJr%(zqM`5!x&I-V^JKZ z>hA$wC>H>OStzie49tt|z!m1ztClVTqpzvOE`-6Qr*K$H{)&M`40|4VCZ7riuXPc8 zKA0VU;qH^5tL<=XeQo!hXyxwE;ICpgv>7wS?d&~O%tv0ivzhi=!1kYRCED($^ByDG zZuOv!>CXIar=U-JnAYvSJxf@-PWe*&gQ)F)*`HZ?cR~MS#FwhPdkBHe34aG|Gcw@A zJ4=fHc&}@rXJZC1?)>)?Drwg{JE8%NNk!(M3NbMw!H0pUj7!1ojwfQCszw((q)h6P z3tH)K4F+dWP}6h^puBPuUuvpC<#hJA9)@ZO?!5RX2E}sTARa@Je1?{4k#t}jH%n-V zrZqL7hik4LTCL_(50m^QD*aKqPW`@eY$t@O<0K%o>qW}&g`?|KjprW5FjNI5BP!ph z)MpTGY=3A?RJu;w#HjEZV{1WetGd%%+OpytgKr!gZwdV#P@U0OyiV2CBJD}_a+cx3 zaHCdQr)ZD4{g6Co)N5Q5BRiAamq2U!6()8lYU?#7&@Ekq1SoaXGm^?GYtk^dAm9h% z-Qd}jJN}K^T|aA)C|U;c65{PS*s*V-t2Gen<@c@X{O3irE4tA`K}5P0nUlIZUT%o@ zs;<`Q7B7|p5G{E$U9=7d=xS&4{y*=BX0nW$XRvQ`rgH9l^_*+P(Fo-LA$P*xKNuTn_ZSvFnhUG zvK>5oNK0p7S=wACf{29xrMmL?^w_lEmoI#~3`{ksb)!hv|8* zyB~5nK=s0OlTs#lmxrl;m>^AL4zHeHswO;Ttaw zGbMD8NbObWDiv>b7fPv)U8JG+*MO1vrUnlg0N;V?uIC6*`tRvL;3o+&Q{9d{e5vao zF%d??PbN%Bw2DRHhP)1W$@vJ5D8k>FtGUyzNmuRKy+|I^c*!fTeT*lTL-VHQdE^u8 zkgh6V`K02uey*VA<+m_1*@F|C)Tq%QuQuc3w81EhWz+@W_es*}@^e(i-$F~No!z$< zz`9D#Rq|S#*=33fO+`6e_8#GLn=e?}GOErQ>9#^5e3ok<2ia$8{%D^cxfbNJmkAPK~kFXeeYH|aIFB%pnaSXRfY~T;M zN$mT~Wuge$wma$Ku8Aw3GuC858bf%~*80E)Z9 zUY;X&sMm?o3_T^$`PlHn>le6|$@M993B5TKzm(q1m!QUPrGrnCbVvgiifJacp>fImTF35dqxa zGy%4v3Px47Ss--idpw<4Ge#L6fWHv0u%kUZ`F|i80wMJ)ZSaz#8jzNf^eV*T8&32Z z21;4BfJIY>+4*e`MWb=MDl?eD2@Y6eHl7eky^l2Qf^PT*bve>7r#I5lHNIZGDQ&s# zl%1YL>pp?w_yL-?iY_}Rx%UYsAxz2-e=cXgU_O%zBtQ2g$P8M2HC~(i0kh^GF`McA z_f#U)+Rqfm^M@Pr6qq}46FWoO`rY4$sr;bDnUR5l-?Y33^?V}5JHkzS8>r%L2or?j z`|8TvruAdAbo}eMpRE&Btsp7ug^`PNqooWa4jih~mhr;SfCy#?m2M5F6{ExFZ~^WX z$6Bnf0xP&5*6+`X0s=UEfEGrUKlBvdue{~HRqgn}#^b*hqxOO^SnnHkm%Ex@^zF?; z)VuTsEcH{XI=R|Q>Z56$1Fb|qguAh{?iKQAep}zHThmTZ#K-Kj6xmy{h&Yh+l^k&= z4Qkw;%vgAL8&)}@)+S(}&e#`qNBJ_S)j>Sob2F3Tp79kBv%airfS15|nl~j<2@@LP z9!}=oReQs|sttO#Be{Q)q8TE(Yq<1@_OAqXGg<(VKTzC)pX{xc+QWb!q&f=%pXHDx zDu_9KET>ffY?0TK)!VYw+e;+>A#NyleNH>mC#t8wT3JAPM^|R^^Mnvsb{YftdUR1K zh=)_K*tG|nZ%uU&;C1dJC)K`M`!CcEaFp`e0oS}GWhXth1HZ3!GD(Ta=jvg==~H|l z&|~?9JG)0b53}^I)~sM&Ia9E&mqNxVx>-gXkkSq?m#z#;)>_jXq*>B5RUBVTa_@$J=Buaa5^v>n2&@d2oyX<|9mw6Ti zJGkhy-DAK`R`}Mg&pKDnu|N;BuiCN0ghBpT^8TnqR58*H#%2lXGY$0jp*btd%Pnvt z1AWAOa_+vA(RC?F+4D7RSnRJpr=276_J{cHck4t|KnMDl*P=th0n3_GK2tTup0LjS zSt9E^y=mZhuKCQzRXI)^snSGU&pd_fEEu}Yrb_8qOu^Y@L-v{QTV6|gyD2a`Dj!#- zWz1|TO~%z}!wB=5GXnN9aZmS8e$|FQE+?#@Z(lSyS(^X4%YjF9YuM9CgfhJA6)Iaf z&2Y5?V2zARHzWcNK&N>c3c+NG5Y#-GP6p~tad~~<_h@$c)bW9k)yJ^2i_$dxy66qY zJrH`6a3V)l1lP%JM{l!@SR@e_fyMg*#b)LlF1{$vJg*!~^?N6Uaj4qBX4)&uaBo-f zj;>TTbld{IO=(;tCx9zBA|{ZU9qW90QP=|tKo%S@pU zQXm`xV?6&^9wV~|$!jB;Ih(EJnjL5n= z3L~Uev38`jX=Htf%EhH*DdD~Rkik-pxtytGu(Frt8)p)=ONvT(<=+In=liVe6oX7w zfZIdkiU#GIt^(!qw&P~Oy-q}-b8yVDnv*5@1al5uz{^9_FI-3N>&bty3V-~Fb}Go$ z*37Ql)3I=g`(J|ko8J<~IA(T6%k@BL!O$uv(oH<=8BJ|VhX}SRyW-PmKgX2J?bI*a z$@ytDTt{i^{(iH(;j(j~BG+Q6yP@9>N+sCz#eCSti1O$%)6;HFv0%-#ZHh)!ROY+hss)aa`(k7sR@AGFoxy^~wSqO8{0388V$RNKFuq z&0j$rME1gfA%mR42w|T@W+x2?Py@JaGKbJ98etNu1Bx&RD5V*m!G|qi>y&3KbCH-P zCg+qj=nZUboK^(k8CEa^(Xq9C!C1mtAcU~ek1V$=1;s=-6z~- z5>@Rm&GcJElE9XJ6U47U--(y7M+;szgo>M^Q(mgdUd=@-&$`RUMvPV}KPo>4WeJ7r zNvfEEH$`w(AhDl|&&F-vw^35+kAZkuVUk5#a{a+hGkw2{AB;SZlZoO=BH3NpfpE-| z3}uEFS|3zsU-Pe$rW{59?DPl#25LP~4!r17#it_%p6vS{gpD`-M)2c?)T#er9 z_2s+y311=*Zadd9P5y$q03M;{-ZEX{d1jgY4cU1DoQ`KDrcDMq4@^*N*P2Gg3Vvt) zEDD~*7&bGnP;-NJ=jg1&fezl|V~C7Y!{F`gI^Q+Y0F%;m_&!(%uqirK5s2UFZU*)p zZy9hh@L=VJ%ry>!8G?HjJ{yF+A4Vh*ZONo8ZHH8Mw$K#lB zL?-i+X@|`B0aYQhh;sVg2BVVYZoruufL1bMFT7}ZYC6Fd7 z0jmoHGMGSJcOZbt!!u4V!a_m#w=4QCj+IiqJ4c8P0*BZG?0|a!Zy`^?hfm;f#SG*C z4*UdNafZ1TL`anHkoW3#slEm(!STvG`KB=7_tU>nI%XI%+L55p#BVPZd~W2jlQ;=r zZk2|y#MQb(hx(dsqa<%!ZVn0e z#O+Bj)vv~1i|syx(oyE^+1GR1&b?aD0}efb5l79XVIgEG*m&(jFJTEIQ%X}9I96R$ z*6}_(p*ZEaSF|_bXC;^@^{aMssFYo*s`#%Xf>RloX? z1;t|)R~A^m%h0blchZ>i?8yn%ZmJQ)%+%}w{mpZ2Oh{@Esaz-Zi-t0qdgJs!lUGO; z(3HKS#P3{gupD6KU~oX&SkK^V&kfS&3k7&J6R5%;5Xu7f#YnX>V&$0vXAs=-f;*%F zDS`yRYw_OT=sU>FUCa8Q0BEHEf0O3m?4PVzzw6nv8|kfk>!;cNC+F zX~{<9LlB#1Dr5Zoc-AncDuvu3J#q+HDD?x7)}ROf3iL^#CnQG@rp5^5Z}3kpJpRG2 zDGRNOu4pLEEKe(rF3+pG(;uz>r^Oxrh-DRY??qL`yaiUY)#@Fc8*7$7YpcmcMsyH? z%TUjYL7l{I#=AUQRJv((!rD$#yJc^Lp`WjoIF-b)iE3YD1^f{Q=7D=imO&2}KQ)#b zVjVzYi{dxPy3vCohY;3B{cX-r4tMjA;e0cowlh7k)bTm9g2@LoEszZ>U(}}d{l{#g zF*!7gBjP(DFpFc_=1pOJ-qoG*(LV7H4JM$0jD3uV#0*zwEM5m$(y?<@PJHFCgMI+4NpYSa}$~cKtJ%g7j?V-M6k2`LWTSepRNPF4;q$4~4!eYOrgV(~;*|F|JlQLDq zm;9Y}3O@CK{7BDvI+P&~XHV;hd##RVxIzjovLizz^{+j$;vZp!7L%=L?CWju_DXH1I!6t6om^;Mfhs9 zEkTRzaoMsZmwW?4M$W`WlE4;DbYr2zLOC#z;BV?fr337<*44S49w@ANBE)m{iWi36 zBUN{tOo1PTzqjMgZ*9@+8yRemo)-XmGS1R;xH5VC5?1^o-Yw}w&^*M^ z&%c_P1iqS?cT4t92S|#023`ftH0w&lqe%@UR_)TO(O&+owuwFBPNdH~@85^Q8YXV1 z_k|ZUs~HR-bI_G2%l!kagiWc(&IV68=Vk=--OZ7v?}8r!nZhaEvPBQDVK?>R%4(bR zl5f}QmA?o5L(7BaV%(08YP$_qB%>RNkUHi50IzXPG{oZEaz?LSwPYeEHBfYNnrO zmqv+2R5+@_;eAH^m0NjRG_oZ4GZ3p?X_T`m0~>>DZMd>J9}vr1I{D_7x{%A98ar!s zR@4HeiaTCDk#u%Htan|+=i1VL*?MxY-;VEw@%9va?zlQTuW}C1!x;`)ZE!1d#bdH% zDr10RX`S}r+t)W4x(m1@)WeSl19qwexnifD>}x<{zqu3FJ2TCAp+!IKZm8XBYkviU zl_lt<ubnvw#;$OkBBo;^my2ZW&1s-h@DEP;}02@v10P~*gLpD;IFUd0yXU5 z%odB$kI2VR?ilU~8kYT-h+V_2{1p+ds6TZRwb@enNUi|zEZ4sT)fI~v@OJ1D_}m24 z4cJ0kA}1wFW(nofszApbxZ$EEJ(@~xOp-U9FxD^{HJN@s{=%Ax-ES1@L_2x{>hN18g)2Z{d~YSvF`Bi6eg!bY6=$OeFFj>m*{ zc-B2j|69}bc#NbX)-4peop6gAthlZ!Jxslss6Y*v@rC?lfbM7aTl83C@+2@e85%tV zBlLb3^XY{XW160^QEr~D5g!;o-l+^L1D;35|P>A88;9Kgxtr+pAb&-%-WG{fwPaQKnrY;_O zTo;QQTXQc&niVhWS#sWKO5hp7!8q;wxxW|etD~kPVjG2hbpUy8rZ9M=K+k8KPw4!9 z8*Xvi9aEg?sQ9SI!ut#YV~3;`QY!pprNE>fy+QH$ihHPiFmGz~G(d57%S=U>i@bU_ zXfMikTZDIYtrlvR8Ctn+&Aq>QL3f%4yMV7p@@5o@F;1G1XQi+!4%Tq{O!C`cmCbM>8y7~u zw;3h5uTRa?MUnhKy99jGjjco!508yOGZ`^;LLn$|3jQ{@#2{)Dv=xsqrZM6T9I6n_ z8VK1vehKpaBh_>YHO6*iAqIrEzsP8*s-0GSm0V~&SrM^Z9cQeBIhJW9 zuw1+sQaKz-IwI|B+_QWBvjK;OSd*U_*V#Q^2Pk2=cUdMamHFBk9;T_=9yrGwp<1e; z?0R=_-^3b#b90>0%$Ok%49H&;6e#a~$429ezvxF<^o;-_s}T754|B*ne9nSSvy6(r zk7=JdEkK|eO9mRlR1AvhOn?wbQOcP|YYig8_K0Z)A#-+%E*9bWsS6#9iIXN%1 z@Z{*zQ5(zBCZMOnh6()9=DawMBy{Ur`>O~Nb>_i27Ds>%1dY0-H;l7I$fD@%b~(hk z!7Mc~BBBV?LhUI9(&hOGXt72c*s^3CRPn}12JpU6NIa`;Dru!6O~DtT_cmRwZQ-xw zx^7w^cQ$R6xPD7+Ir@I92K#i>i!OCz2KTX02u1CmJzq%0JV-)~vRFu6TO||-aq9(# z1#@$^8(}p|LObuP|MG1#F-C%5{$V7Olv`ck^LlXm5E*fU>2uWjXBY}BXZ}|p*5BAc ze*o2aaN%i~-N&28s-nf_MK&>|pAx>GmP$^4c9A@p%61ti3?ecG|L^=mb_4=nEZ;X8 zMflxZz}H2aDYaJSo#lQOzRUB;S!V`F^%rEtD(Ea0W*=w9kK9z_1uV!?iO(p zi)Q>7iS(+ag*#@}sZ2eQD<|eQs6})WF$_zHt`S}q5P8}gTJMvmzpF~=Y2Ou2fdV%9?qJ;jnDkycG+*i!Cr@xRxvbpK#pT^`Q z^fY~$wCbE(x%~^a(1!qsQJ6Wj?+r^X|B2nYVm61$4S3)F(uvXR5 zB&rF?^{bG?V2o(n$f@tm?XIRhu)9*!(Mj#}o!9+;E zR><+iLZL*?I_M|qo#VP_2ho2ZF;R+%& zA466Uds;_nZ`go1oMiFM@fiH$UVjq8SLl#buP`hA)f#zc+!Aa~qKN`O&zf<%kjfT> z=roB$@DQEBFpn$lyr<=$(HQPYxAEk)Tu(rNBZ@e~hEsws{I3QO)q4if%rwz!>miXl zm9$?m6Ng-6QH>E13@ih5>Gl_SPe%Ok^)7AK?W?^K1v%+6jxOH-I z5m&KQBo|w`Kj6Gy87csqOz%Frzc?hle~<@Twhe!@a5e&g5QcQi;PW%)>j}rsNx|U0 zsC@f+jm_t%$63d&emCkPE;ePXH(=0d>V2jnhpulH*5(`m9~n$(RX5?E<$|4*p)Sp< z&SG`GB5#b7$|SKqkyphr%#87x7z`ftP-0NsIFC5WDLc( zY#*(XTKEkQ%i13oZcv-a&eaOhoqku>n?Bem(_$8Ans)(N)FpX}q5MAUZbCmh7RE8} zaxuj8u=k2#FW@^Fd#iJmc%G4`!Li??L~SpcKCQRK-?yQ(Kt!57`@aA+K+3IvV8KECCAaN;5lq@%?L%0bUpqnczjCi_l>m2yFZ+C}2@OnO2-acfpyTtMW$iW?Ti7C#r!<4L{Kbr3HBRr<) zi<#cK6}fvFf%ni*mq8*{E0fPZo|ByM-7#A^IL`K^V~ z4r|_kCojx=Jt~%M9!+NeU7Nq`xD|hahclQXcPaM9!zJUcMifN+nJEOB7Vd&!`gcw0 zfN09 zxEQBYHRS>mmwyw=Uhk;D5CfKYeKOy4V(M|iD6W$1$S>P+Ver&BAIl{fbSSixUk|QJ zj8ZA|z196KYt5BLpQ#-`bZDf1V!ID3oj!qtRyo5{X?AGD_h3>V>_zh_>zRVI!x}0v zYPsR<)Zk+4=4m#ITymDW*z?Wmxq?_RtAFpd;=&tPnSY>Me5Ar1JXfL2{w>+RmXDA?&GxF6swxwk@)>KuI#9i zKh~%9@qb3G_J&D;x`uM4M5&w57M#u#SwTKGFsGY?gnH}XoPuAtoTk|K6yf~WnQCfk5Yy0+{G z{-5dI`##2T7L|2i2uSqrPi7xn{Q)%cZwf9530@;=lb3i1WlFQM^*NObO~Q0h z{3sV~awHLFVuKA_qeKOiIh6}fm!RGJHS?k~-jct`uv2rB_sD`41Fjg!e zhJWIE4yjBxlp8Q$YE<#B-q1aS|Mv>a&LXqQ`z7n^#3e_;QKl+y-fU+pg&iu6At zwR3hcw6Xbjy?Ty@rw@ud>d$xM7)=u3f`SmbMV6M0GyNV_@Q{!Yqb(8vVK7K>&LvzH zVH5MkB~5^rf^Cg>&qaz?o`z`2s*TY6=zoWRb&vhLT8%jG1^%l``Q)_C1c*~ z_l*13_DAo_hwRMPNkJH3^&tPTAux?y;%0X|c2*V&du%jXF)2<0;$V1i5tB)=r&F|e zTi7kpwf;0U{oZlOCeFz*942J9j2OG#%LTA5e1+};`-#qJ&)#_pi3d4%hOBlk%U^le(>_!@~_-py|H z!4Dx1e+Ybty&ys#2{B~8X>5E$OL7t)l~Jxu`b9{7vb_T&IjPrRff2-I4MPpZWsJEb z_Onp3`uu}>FOis2wcrzM(8&z4(tomSwk>YFdLRll$wZr^>#Alr9StJSb2rdD_btS79azpt9daP2Dsx3-| zKO~VvD+)_nIoczy6I$Rb=M5qATukNbbVw{X*WQXrWZaM@C~@A5V9*7NMwF#C-0k?W zmJ`=7rXZK`R&3&YdMhj&RDZHqjj&R&h(`ejwWN59TTqQ<9ji^k+(eim;#^*#N>;Slnc095pF*5#AD#xD<#uJ2g^vZ6K?@DxlO&K1}y`ooM&8! zJDqRq(M-1z%>hisS!_2l&qTB)BC2bHd7a`?T7Qc&Pl|PHGSj04 znzo{`)FShjDmaDJWCqJndBPb%Hyo1!Wml^WpxI}KVxU@$vI5nbDOUqvCMMNO3~;(Y zQc;9mw6)lcGHVQg{;pDjn^69cS!)cKd-MY5sopY>q58yT%|5K7--?3j52m5|fa<5* zvV!W5af0sjvy<`^?tf}~s0_3*Qtoj>^+w%5f@CIEUQxXA`3Z;}0SI(fY3{+3eo73) zWn7TIRs`o!zH<5%?DF_k?5can3>>_+1n$&-?-0qQTQ0)#E@v6tLk&r~w)YxhgLsA$ER({Zk_ z1AR(1KM`tlzA%_%T8M3xb`2e8v|_0&IW5$ZR9E$xQCHYwJXXkPvdYr59kQ{4=6qw} z+*X~|=1MK`j55v$yUaDF6fdgUEL(H?25pFvJDz)EVSi09f3jNX_MFLtCTu7Vh7(ds zGzZ)Bbfhd+_!Y!RM}abJO1dP!qmHzr+S4l6<|w-dT+sSzo6NnEY3^!qhT*Pp*ABe` zMura{5R+TA&B9MZo>5KQqxx$~m& z*hnKa<$n`i>+RMQm2S?5C-kneS5-N&Gm9!xm=L<8lIE6dm9^v2l?}*V4;bUjXpryn zMyHC~H|f3bJNS=RtLY2rYGT~0G^HmeD7MDH=+6h{Rb(NsYa$J+9HbhbZt?NC zXfJU$b(u8w9;wP=njG{v9|$$oDW`*7x|;D9{PIx%mrHlhKq>Ahy~`vsneCfLJ_&^5kz2fH#;x_-y6Oc=D- z#ec3eHD62}w#cU^OqJNe3^WjWFYG8X2T$`M5VSZV5#8mAMGc`ZRLtNAKg&Gw=6gPg zZ{*t#w#7~U08`y5aYmjh$f+_Ji@UHdO+_Ohbu&roI-#l@k?!9gxEvzgU}4h}#q zzU1HB87Vm9`E)^EE8_g?Hsf~5o>tiMyRE4L38xSdk2EHGDbzg`^xU>|3h865k+ei1 zRb>!^0ok~G_u{;u!SE-cCKcASF;!&sq5BQ3vpLyZQLbM+oklq9GD6`&M1+ZtjDI)o zjIQjpjz3|FLCe86tife1rv>mIzAV@z-D8kD#KHFm!8hEHTjY>CWsqBONWJ*P)4Y=9 zeppvVkalPnj!~69`aA)EcPP(M{`;h!BBUUAgVltQvV1{eB9m_V|&MhHYKY~IZX>BTPs7!>u zyQ*(u`}R4J!uero`cK7C=6@z>Z7En5+1cr}oEOfUwTvx(fB#P)g+XFq$3s&MIR%RG zTY*w)u1F^x)_4KQF{~iPr`KL;JvOUA!)Bl19p@=a4SF1Po>FM=O~;MgRkYsOs%9eO zY6s6AtE*<7GQAR2ulC3RAJrG_P1iQK5Z~&B&f8X<>yJV6)oDGIlYdFrIxA4JYIr<* zwh;4>ELGcp%`8?BUvtK+#h z4(aE2#2PFAl<<2)(du=GJWYcm6DI91Eoi_bS)~zM%NCje zVUpR0%Qr)sS(0JHF2Lt~tDknpI_gpRrf%MuYmuKTBzGZPKX7drv!(*sDe{L&IAl)6 z8(>j|^-vp+N`JUomDHa-;LXM`BMlpNPVXZiWB!7-srLYk0v?{Pinwui>!$vW9XH)i zMxu9R>r$VlLk9oIR%HxpUiW=BK@4U)|1V~I=mz9agaQCS_t%CA{#P^p*B=p@kUlEQ z%lza`-G&}C5y1>La1sR6hT=d*NeCnuK%_LVOXt$}iGR`4NITt(kh;w^s4X=$T2_BE zY2DMnnsO2gs`Yg(?KCS}>zb}D^e*3ZKfe;$pOfB<2@+3;*N@kbkL|9PP3PHr{*zqq zldzwT8w^C+y*_YD`V(nog5Q`6G6|&)IGGg-C=(~m1Sw7u2mcRa?--=Z_oR!qZQHip zyKURHZGYRg^|ra&wr#t6H+I|ZzQ4I=?wOhY%sFvC)vAgWRS_$*Rz8_4GoN#U!va1m z0MT6H@2tgXn0wn_D%p;Sr?=Zg`1?(Ota2x(#Ijr8B7;IFsX&`+lE;692 zL)jZ+QYWpOe$mq@aS#vY9z`Dqz@tx5FjcBWJbyEJ=NdbQa7z9P+1gL2uuuy?`Ril~6hPjhi&LVz3st|*F?O1{S)vIZq9e)pj_(9l zD;G7*2C)3ZgEDf3#L+I1Ta;Q?y8~a(tWT*|{pby>a%>L3s}IRv@MwyymrpW%UVo4< z6`BymOaKqSISjA2*gc?O`H37?Z@P$kD1WsTDSpv8-OB%$)n408i8PBXMYhQ8>B6R! z)4t15PvoJs7QSVxjosG1(@|!o6)Vj@5=#EuX_QT}{oz%P3ExoDZa~7Wk!XO9Udp+#F6E#$6xYt(sY z%eolXY$@DsK75dn!OJT6yX(}rG+9!Lh1E^S8{F0bHiS!j=*+UXq$+hOl6d>pxK{q0 zSo<*6xcY_H5*$CRqXcHTo`2|lfo_$VwmPB+v3M>mUL3O?(@>?GrQz1q96h!xq0 zdK6mfSv`Fg_1`Hp@#Cw;@k{$)B{TBaO5kZu6QYyWtZIU>oIYJM9DiH38l*uLAyO4M z?qe!-e}4(VN>W}CNL4M6Y67?yu{keO!$vUkLf5ctfT*OF>TbZFZJGBmmZHUyrJ{g_ z_bu%e*%TVM=(lbHCvEqvajxW9Bd(i_&E)ufUZLY3EnL-RMyJ zf0?xDDJVS*gch=CyWa;@hfY??y$*g7+$pyL!hGtety2`s5K5Lb`cp=`Wo7 zJT}N{%uBewBl&5T`{T@?DzMxP9XU=$(rAM$GQ*J4m~f&qln7=iL-zW-;(6OyJJa zqYuJHr+>f>UO}^B$5UQ1(Wq}l4iAZUBbs8gu7v`c!Vcaa^>JHK4-ic68#ZF>xHmEN zb4xFDB56!C+h=MqOLR`V-U#v%9Q$TLeIb{?DbYxJ+)-r627k9v2F*&m>BrqHc_sFZ zG~XTL#9YA1WSYAnc1?qve!OMM7nEcp3#IHasMnYfhqWTw+8U!$%MoZ}cr^E?W7 z_KDw%{4~SV+gf($PG)X?fdt(dp@q2we;x>Xp<$uLTo58sD77@wr=%so(hIQ}}3glnqV>X87 zywRPx08>&9c)NotZA>eeRfrMmHdcsgEHE7xS9j$3Q5`q$b3-n_n{)LK52$-+ zO6H9jJ;`i_p8ShaY1tKxm)a3IAe)DKO~Bwfgb!WKL-%%yi6c<{@NkGlpQMW=F@I8i z2g}np&S>@7I9q>P&GVBS5OzRN{%iSd?GraIV5(0<>aRBP`J=^m&ujpa#COt9>hIgk zM!C~9vZpVtZ9bP~RE~s-)3QHh2iwHIe+-8416Ixn9~JU`Mh!q<6mmbaFmjFYLmRnS z=>}qxohHr+M_GSq)J=M)SdHr71%J9YCF{taIBu?*k$dMJ?F}cbz*#>s-!Ts=aknrm zp|vTi<^cgN8LTW5%X0>{?Zp&9JhGo9uP8H8<9ji(IHIXei;bkCE>?tJP2DXzZ(Zwx zc31PH2dI9pcu`gF-h#^@xo%vf(eAnzaV%Pw*W~(upK~r!Hsh={r4NF;YkveHLvnUi zAq+ydi|ame{Q*J4Lj+HQxZ|*FU;p!7_(-V1R$(gqb8m)OwNL6~^e7B`)qAd~Emeo0bv2~`6J&RN!#KP@XBxt3o# zU@o;}TtN@B&kOo5@`MvPLu+ab-0An=ggkrggeaR?4$4hQ+3{%1#(#zBr<1c)%Wg;X zBqZ2a1+rA#)N*S>`2vDyp4+{T2pu!_;4=1|C|--za+0}q)KpkhbT~j$dw9le`SvPt z)kM^G8Gjv4)%L{nzBm_*{T{99<|xLnrO zP;NGSSGF`9>S7et1Ao7>j-$86RGwOyqH_7V3e57fn$B718k+M{@)@3%%yUt8Ja?9Z zOlP1)c7U`@-K?XtyXA)FF@vLz%da}WEyu&r`N(spXY^m_$%hz=6$(UEz5-hVgVRe>$Yf+52x%mU%o zk*;$@coO@`eKn278uFSCWpG11YOGBXDPICc~HpfnOrb@htESxHE83 zzQ*FOr|#&w(FeGBeGx*j4`yFI1O$D)(=_<`_C_I?R{hlg|R*i>%5bJRyEk|zq5-`=Q|_scos ze}rz_T=~64vrEv(;OAQYe6d-fW>3hOz08YtA8YuC?E2GGJWm|OS_`~WhJJ>ym6p;S zBfim0(n3}=6qNmv6!VBrZbIWp4{`xFtp#8=BPBB`JAd0ds!vUJq}vxN2$2xh7Jofj zIYKY8bRs`L8QNp}h1-XlvlgCOoVyfe%dnnfbibXePd^)<*ky(swU;37#q|P-KV!=B zN*Bq6u=SMs2AZ>Zse3+9F13NI^a?xd*0MFR9AA`Y(RPRJ>V$u5Bj0<$6dl)ui1bxm zmTy2x)qg)v$&CL9iV5UtUEu>9r+{WEpS{#yVZVrPes5VR{elnc4*M=8n2j<~+xh@)cS$BZ&j7)j#gpS{1OMhqE(-vm^3IF{59iBlbxWv@*>o1iIxOM*Q+u?)SIbrH17}QZ2%qjLcCuG zfmC0#g2j)Bu>O_ta`fOzuaNcz%02x21|r_<;oO3kKzdiwDCt>Q|9?Dj zr&N(Sb-o2CYfJxl3yta5E5c%1Q32vI?K!V!%A-m~iwHhHKpD_2)qYahc2m{xt0tN1 z2NuqS{gZ${*UdLS`XDZ(8AeHi-pc9;SUY4ao5HfESvl9sHVid3tar;Q9_$c>5^9cH z&|6HhD`WK}3DuWTJ=XntZQut9&VL^g?F&K^G}R7d%Qw9&}G%Q?q2NHT@c*&kMlhGm7s`9Dge5z zn=v_><#qW<-gtuXp`9G0RoRd`r2cOeyi$)Rl^EAc6vVLK8kQl=xOjVdjzR)G5{8iy z0($ETnE2rwVNea#mR;Vvfq!I&{f?M&Vm^d5{7@e|33(4#R0LpPeR3;-TJ(l3s7#$u znj4@Fy5a);pOgdVN?>Ouw-YJ*&s>3@V9#-cMh2cR{a?)H-=bd4xQ;tfJ7>_bQn%*sHqII_Fl3KEHS09#!74eO=%!3yKO#90d42;P|Wizkd}mKoKh&#D!qt zzJ!B%kSe?xV4uBVz``5siH=M@SFBX47anlzmJ=1vxMsu)fCh48MF07n$#^M42cE&b zne{|n4`ht3y@Q-mwQq`B{jDA77@0mQNpE6~9|-R8CP&?aP+qVjzg;q4wEDle&0;GN zBARh{WF}>eJdF zq6mo^t*bfXr`)b#ki@Sjk{R$ZY#JjkG7X z`XiZ`AhRl4sgN!wA@^(2L1|h7HGn-Tfk+V4@dt%Ore?;0!8IcVDbLUsQ5qi>M0FA6*n-x<3?KOp4+A!|tFn(gq&2&j+0 z$a1IFMKl^Ro)5g}^VfbWVEfQsvLx(+Fa026bOCj(6r8bp`m$KZ3YgGK`L0>S`SE|T z_blllVuL-d9ZVI$k`VnMRCZ6ILsFZ82|o2LF*lBy{?&+Y{0U0o_>8<^4PH0$w(bln_ow2_ytU?bR~D6) zMy+Sf!|{wA$c3X(W$sqLLRu3cv0zN)*9BV&Ws!E4>;sp8#T?_Tb}`m-uZUJLmfEb1 z^_+(_9e*N`j_6PM-?|~1(ffEAJJ&fUnq#9PO33}%BdekdW*NHLv@{b3L^zFA|CV7m za^ZF2pb&gyws(b1Y#cm#GYNN#JUqkTr97ZI5_Q#I6LZ%!ZV~-PaT8vbPFa@%Efe~J zQ(3xs@|yl?(;ai}D9HlVX?qJWzMr=}u!J{7OMl0x`%YG@4nb~Y^bm&Txh8xOdfeRQ zRf6d%K)Jb%6hOSZW^#ccG)t%2zI=v9C$oGnX1kkt1yfFx4TGqj#W~FDc`wkhLSBOG zXF(S)w9`J0&g(KaBA4>Sr+dKpA(IZ<0sm6rblb=C>;M7;#0CxoMEE}kIAt9y{&zJ~ zSbvI*!=fSD*biccn!QQvf#5oY3k{|oXLk%UGntI!ZE}NQqsQE2N?lWP4O*PRh(eLL z#UZRfAmOBEzA|<^yUfzksq^fQ2gCV|fItASWg)f%T$^!w@D{VGVq8ghCz}VLgLbAB zL~S5GSTFo+VjYPAl-E$x(_AzBBbQj2yMN;OY^m`hn0SYo?#`Nz3vKq70Nc4~hl$Ph zrPElV&kxff0XAv4@tC`-nuq5e+F!^B#E3y9-qt$U@(CmkyF6lzbG(NaUG|RBYP~gP zx*qDFNx@ig-4`eq1h~q&sTL2$w|PniNvG<$2G;5|o9eo(hzVih&~>`0D1UU5Ofv6o{P8-B z1u>s9i=9)X;dJ~&NS&;hf)OhcFMq+u*k+2hvoJ!uH+)afJQ#1jb!GiG|GH>UE$tzJ zvS;s_ZOFf?6gSHoCD~@X`#>##4%-OyTaVm8`4#Cw$(UjyQ~01G*Cb05U^|?8rD$W^ zsw=0BMxh`3&iEOy>V1UIQx5l$=n9C5!bDhv#waQlKfT8~deh{VrKq7y`+q3Op6K+m z7tB9EXF(G)6N~Px{wgp@jruJ`tiu{RotjjHCv6eiJf``8O|gPsY8~?NaH@aN$q=UxEg9?DGxIBIe@s@JB%9-a6)Tz&=F3tL<}J+8mi!4yRB0DIH`@Y=zvl~WZzrR zPbw#Oo#85?-s$PAELIoum#eQo=z_pgS^%cJpb%_3bG_*pj&_o};ux7Ju%#``cXF zN+-s1GL6O&CUa|Y?Q+x7ym2n$TZ9tH3gdAddLjC)Kbk8corf)oU>?z&Il|zSd>xzR z@9^*{HUi=E@cw#a27f9ixeMFxFFH-j67_}1K}+qmRmHHMz2rLCr^F#*h@g)AfP+p= zW2#RB0%&F$VS<&ABWV#;R0!U31E)XrR+^;>vI}$Cw=EFSzVwmZ@Z#@{rjXEjiV<@D zWIu4}duRQV4(^5mI8_#w2wGCDLd`aCq{B#N>YZ$ZCV^3po`2>fJ@duuW1bFjg8FO9 zZ(sHyYvnwdqgve}2?Ugc z00czw|3k$8E>QLJ0NfRHeiIoSFFyU^b!ZNE#auyCsn%>Dxtz?@b(X09Mm>jRXhZQu z(R|Tt<|ZPVqzt)9CgYf-xW2NIJ1$mKQ_D9SdP@jXbh_ zU@M2uHtMxKG{jSVkn&avy1+P0i4SwMoSZLVPICO9iGOYf?T$NNfYAtw@%FYjk}>kt z7|S5p5zPSMh_>caji7f_(7@sxmqZ`tD2L9UHr65H4j+1NsmZ_I;6!G)_zqr+_46l%27)n zcEcLGF+Yn*=18gqjoXka2Zt&vd;R*w=|nY)Re!eWnV@aGRqLvX>Q&F3W=zYV_jb<2 z7OnOIGx{n&?|YqkLLccqUel*N8Zf<)HVzunEOk0hwIk6Gc<|G({1VeR{E|-8CuFVf zd2%bUA;a_RWe=i&j;X%u^rF~^Hf5CYnbb>5!{WW8hE@w*X+>E}vGoQ>I6t|}3~!ma zLw{*w*AK;)q5ag!U)wG|YC$5x504^whm#d}fnUT^>_QkAcH{x{)wZu;izZcON1Z%9 zR@I$o{D!DC~o0tK#>K|h>)+z z$Y@e zqUm<7cE(x!gEdT#l?rusr;QgEq`_BG?^IUbu>IwQq$Xa$>nUz!u=|Y9)w)Ucmp>T2 zM_*ea_r_o8{V5M$SWcJ}Fn{IvD-IKyej=k|*B>abe8;YVKXzPBhGu0iM`Q1($A8FP z!K-Hqrdh3k;n^wygFxVR!N%L$n7OSLh^U0C(1^MX#n-6}h_m|*{Vvg;h-H1YbIu9v z+n2-MdaaJ_`@OYg%kXY*4|`^JoyW2}h+l=;N4=QmR)H9g$)+;AXZ9I_${rM;yx|;{ z8>Ngc&QAOHY|Zru;_k|hi#4OG>VM>lUnj=DvcNOpB@i3K5*O&5%SdHKw z_kBao&b)FjigZhx3;`sc#!3ZFsmUv$r)n=73o<^izEh1WjhMq;ONFr;Iv+?{MVzx# z{JVkfaQh)#@W6z{YPg5Z-+#AtZpr@3tWpR|1TBg@ zADIp*|l*|rtkwBubRc-^I82I!)o8Ox*}s7fE64bD}Qp01@!YWBO*Z& zbbj{7eCX>lg7p&IjgkxLiP55Y4um=cFi2tcN43AZa3rE8aG#3TML>k&!NrNg6r;Lx z4BojH`8$AhU7`e@Te>fgY(?*VA`xZYJo=@!zieXW(T{{BP+-s!qi|Bhi-4Ok6&Qu&1r>&#c1V?SkHJ!sb`(@IZx5r@(zQ_o znu7jn*La0^iR;d|AUbTkT)E_yt~Tao93tf*cD!?b{Sp4OOOE(Dad9e*vFrA63F>pD zL7UJUzPOY#vNj*;1Jcxc6VtpmenDcWg@>OKO`?xHv|=&3g?|kbGDee;C-d%Ef4qdQ zO963bC}DXrUb|0-TP=gq6kT7xC@7__m!hH!WLZ^|D-pGA4W}Dem+1Ctg@{9ahor@vzwQNb8j5{!``>StAy+^(y$Vi75FP@3a5VUgC4YKl+U6&zKJ}oH( z$ox8z)dv4aK7R-=Y%&3}fBdfQSR`jsoe(#O5Y3?_Q_@I(DtqP$*Pcw7uBlSc!2o3B zBom&;^}F+KQ6{{0R}z$Z5+~S$*9*&PG7>3|m8mvT7I)kub^uK-e_ss=VWG3L&BgI- z3zu7Y)GMm#qZG*J_K0W%kaU_d~=&_F;;|Fh{;@p5rBvr}|-_~*ve%FIR5$llcUKUfC-R}*NxGTRpo|sh^TvVA*!bm2BRFh(4F>;)I*Ig|I3skS-;zv0qn7?_hbYX zlQE4Sz<=iN?`sNpGC&I=_sh16|LL|%!0B$z*TAuB)pei7_%OeDQJ#YPn!d*Vi0 z)2iQf555GTBzCEG+C)EM5>=mTeLN(cd4FTOy@Y%f``|<=QL&*aaMCTPvptE8ozj9e z;PG5n;K4hnJ*^4VI|vX@>FTQ(UgZf~4xatInzh_WT~|sHmD;~xjRxJ!Uo^v#S7Z!- zL9@tmmqhc0VYyfB{mX)9G`x3iL%pL^hMTmfY70b9X{+^eb|%%{y^P*2t#`I3+<#r) zg1&@?!%*6&a(tguE3uXYRzgUA1m3o)Jt{St-j{Cv!4qj^Uen9n3uF?9y6~FvmRZhzSa`CPBgVsrk?Q2E^+c1dWR;4*{}-Ib zM#m&6DIu&>eE)C$weK7%VJB%@-%tEJ8uiiU$ZH&mSFPFY7=T4w%4!4qi()~434gMh zj$bG)js3pyZlg8tGWp=Pw^{Wl$*xN7!dw|-y#x=aZ)NA<; zb3L5rFim>iCjd8(wM#L>V}C*LvFK#Ti;YE0gs8!4wiLP_e_!5B2&s1#d=tvmBywyzyBqTver?rEpYCVymQRAO!!tU$Rr z`bO`Ztbl&*PvPZyEa#})V~t7bW+e7;9FjAlYr2-L%Smo;-&p@J1AfNe2+5P<#PR;0 zvKLG_NW(8&2^v!0lE3M}4MI1;UwkHhkmO**37K0o2C&q?e}zrKfYQ@ziC1S%ro~8ig|1pE{-%Eu>9PG`lEZm%p{=>zL zOR-Zx6+#-bD0q*os>z~@fTQds(&zq>>`3martqhcBctfO z1$`}wW%YnfHOtAQU~OW3DEPX%ewy1e=mRbUnc@NTFYX(GoPQ)63W|kDKzqs_@MVFs zD2(%wH&qNIL(E}eLB31T>|TI*v46jLSVefj(~dEv<8t+d_@ljLqb-*}*lc?dGo~PKJGUA48PobPVdc&z zPZybEzd7(;nqnbs_I7DyV2{I~cTK#S%SOYa{cXGb$7b@a>O}b5y58E=RpUOEikIZc zUGFt=uL)4{_h`fA%>RX?N_0p}jPCDMe49g`>)Ex08h<9AOgVEQ)EZVSEkW2x&3mt3 z?A+%~fK&svT{HH4{Q=0K)!=fOL$LkkvQmgO<+4Z_rDJ&-R8B#T%Q zH?f3T<>yU}Xl#VkmLvZ13y9*jK6&s*1Ml6EMBFbF^Q@8*`{8WNBxLR+T z^^tR?pnreb_J?|S+V&HznMpp>>E4HmP)sKjxY;Yx2ym1LXCA{8E+AOwh@=~VdxkCE zHcWxxT?L;hAo%0{i;e*d^DxNe&JGLn5dHGH#NOo{?GYA@e_FoqM4qb%%Pk5TZJ}p= z6m{JRos1$xmPY7$kRM%A7J|BD^b%MYQ3O|kMt_J6+f*Fd*#N$Hg2<(!@)2kPCXt$a zybzA19*nwD2*NB;Y?gH}t}!~hg@DKln57m>YLZBed>oWr zQW;`Ha3+U<2ppIN)!O{$Uji)>pB0Gc|G)q|G$0_V|EubYd77EH{YUF{jh6t_Q|zC7 z`hU0MBTW_vTR7y=z(iX(FeqBUdgkG(NQ-SGaV@uu#fRW3bvf!jTK2-CD}dH6JMr3e zJ}VQ$7KgJ4b<_2x=fmQ7>Dg0ujA>t`3 zguY92WCs(>RW3NV^>*xpeFJsyk55v%4}Z5?TZo}Rl^Nl}vRh(EJiCH&>f_}}hlR^Xedp%8HkH+Smg$q|}x`>8|`5UW9mc!0_mkQ|{9c8fPxaB{%P z@(XwHR5J*6`r8ZK;|R9q5Yy|4T;Gp7w7%hej&%r@rOeAee?nory~2>Uck;NNT7U8g zB}8AHDMSlg9ZS8S6~J6&?;j2I>WGP2Tzno1S+! zznPwtw^wKIQ=T=at)<3Kath5AI?fCT|07wCAk^8|cyC__(a8&&*~@WnJl$?fB3Yyd zB_01S}ReSQE!K?9dp(A=^drB8plTfJdo3hz@N`h>3i-eo^T42_~!{X!LPe=J|LYYtX51V27b?S zGA=5!wg|F_*T~{ZY+`*LN`FZ&ES`>{wMG%+p182Hp*!Fn?=kfn9-Cp>EqhJ4W>@bG zz|Bhq*B9n{|1piKfkjNDmMD>MU|!Q0XfC??@(go2*=>2e>3ariAAsea45#o^KQd-+-w?l6aJv%WLynVukA)facK zXjHG!t1z)8PO3-K8h=GwFOP)$b{vZuRjZGq?^iO1RSx@AqC_Ai@)c9| zl^pf2Zzs7|!xEk8;-JiSF-f^Eo^@8rU=i+yN}sY*tlbwo=nq7n+8wX{7lA~jQt4E- zWjHzoVL_N5sn&M9RDC(82cKVp*r(74;TpgO1&P?2dTLJRkYuAUjiO`K)m3?)Pvz64lqp{>E~BjS z+?VR%ts5atRjnG7)z@AJvz=gXbGOtnoTO&2-=&>(MW?RHOTg#po7Adx}$bIy`Y(+P>&({XzI9}bv`z+EQ zC$|PWF@N?FqG3dSXMYn}n8ajxbIm+Rxg&Zlct zZw|aDUog^*0E??~fp$QeZ^5Dhi5J0WUR{S1o_{lb!?TVUlHMnu#;uDduNTZ4`K+d+ zQ)ejA9@i9PMIw#m%AP+-TKSuugx0N$D)(r}BV77%E*vOdX40f7*bfWsxnM>Y-=Z(h zh0zz=$z|n2pfIXaZzy$%qGYZl5K?N4tv|X1mp)f)1Q9p80dra-m0RmNtIloVkrY9~ zs(*Qf$H5z617;HA(z?p6Lpi%@AZSwHI0P{U@8mABMQ5zRnEZ{*7iNLvhCd%9#%51q zvpWAXGNUQ{lZAVRXa=@LRkju7j5y*4c|9gCYm7-SV&B=9f%Lr3cRd1&_7m!86!ih(A$iNrKnXF$U z2envjnLcsNxxgS;PCD#YQ!xR02uA{Ta9f6L+lxSsvC9@iA93pV(x7RdH_|#e{ePDn z{asN4CY8mer8+oA%Z(rlCg7}{rGSD&r_mW!?Hf4JBc(v-wx#gg3K-$hC?Zsm43m?OKx^M|t@;NUWBdg6G=CS5|B@Wg2mc)W zfc#fzwQLpx`)}v0`2S8X_P<~i{lCL1`u`V3IU2cIGC2P4C<`ssygcskq75d3V-Nj4$evpG-1uJj;NhMt zPr6zrk>)PtaGObczp>SE>nsQnee86aJ-cKis}6L!eI~}TFxZIZ8v5cwo8-sM^Z|al@wk@`to2|c8~y(2 zfxT+1Xnohozi_LmgHL#+^wK!kv1wZS=H~Ygv-oE`y3+gofj-h!@W@`2(sHlGU0>p6rsf2 z^-)GX&`Jj^tV(VNa6YY+<#I*g^HBcI_$!G!mrtxnzE;fatZ?-h2zK`QqGs;q6=$=1 z;nHCx>Dm7DA^~Awl2Bi~B&LDQ2c7Z?Fx7$D&RVtL=XvDw;(rc#wwZQjH#r z#MF(kJZ<7$z&o6cI2$ttk-pu6k6Z?LLQ|Nqq{GkRkpTtjn#8b^GKP82xK+yjxxAHV zo)?ed)HlEQk)!1gfeDoD9pjr1*K>1C@O~_DeliaW66=xg(dJSg+4xNuL+H)g{?&U~ z3u$T61Oo!P{O5lW@Bdj9|09g~e^$y`bsY^{HMDPemWh6*KPb?WMd%I7p@3l-nHDJ} z>B_k9n6lt?9A4RQCM5I2X|Mx@*K6(G4{d{D*V@ka%bz=;j5a@c1|$@#Nfs^c(@VeS z-BaEV_xp=Ig#h4Nj$S3E*cUV3LPMwr-}VwiU=MVi0hp>mL6xdjxb?GnY%ap(JL+22$NiJm#Gmg zBw;Lcg_mcEkmJ%m-X; zDp^QrBqcT1Xz2AjmV?JJWeF6e)$o*DW{nIdt4n{2D=eUOB+CgITEQ&VIklIePmLZ5 zO)%?f2*R{Hl<9(BwhC=W2MSTMlT4%>u>}{M9COjFpdO1iSyrJ-(^|yC>5qG7_sZZ5 z#>Ym08QlqTRu!7@6&IS%1ebb|RP6OO^>V#JsLw3kF*^yn?NX<{z)gSIkTDOv#=OI@uZ|C`dW!K{F*%a(-L)>N&}s!+`tRYmG32Q4a%pZDL$!6eWxu?!RQ7-2 zTCH3+q`7vPl4a2(Vx=df_mlq!ngaqXwN9^!o$C5KGt~ zz*%FNZ#&UJ+C!%T$?kfIPNFvXCM=S@@HMQ(Wyr3|->m6w;dWSLXD3$99ZN6TYk;rf)pwF=U7>$3T<&U98t5>U_hOMLhV~^h}zC6FJMqQCW|}JjM|pLLEX9`AL6tKOxqj zm*9*a^RzJ~*2H~JD24@~+@ zUJ*AtsezP&5BLw}b-o^4J(JZj!GZc5i&^kv1jY=(4=63bbSi^*TFxSi5JL@F@3&1#U_>M$fQgZqQ&*GpEJ;^rt5(-rl9s`dvVZ;OV>=+&B`%$-tvoNqi7#y`d` za06M{WT*_MLhUf!FyTQ?b7AwJq5ejE^ZNnxMa9mZR7S+s`EgLt82*1`BCGl_tlMEE zWa7*#0n)M=L2=+K4=D;_!dwSYgHa8t=3&wCC8l9XuJtCl%8YG^FlnZZDHf|w1Ni~E z(F{SjO=LwyrQgm%^ZJKZIV_n}Z>A?YLnDFaQ4}i~>PV^YEGocVC!-D*j3(v(m~rHI zX!PJ&0e!KC5B47hRO)|(jYAar<0r<yB`^6K`7Ty)uir1UJn1!?%W1rt zE9Ij#(ib-AhlR0jGfXv;xo#yE$4}R-ykjcI{)5l1OTA(E(~}rX9tf=EG#oV}a$9;I zVE5m-DapwU2_pF|t&T3&S5be?k+8vt^Kq0Ci77!* zk$LWwT{M#KjJvD6Z}Gi?ekvVugrUg7=xMs2%=Es5XBF(#I{>|KdCa>xN;@XY(zkPj z`ZMM>f9bG0%wLYD#^GMu$?R3W>R7SHi zP>Up&E-C`O$(zR={;IlbaF(GnR`9lxIxfO+| zeau=nZLVSmQWUge)e0roY};zlls>ZMXuI6+ujFJygRg(X-EV|kpG1m>c{A}Z@pTn* zm=zF+f7bWEr~3TM`YQY+DR$PW6RQyY_=-WJtnad#%*aXtlidT7}1|! z#fXMlGL?la%VNrMM85I(gzSDT>%Jvo2vXCU`jdj#`-b_6&5+SPXUdbc4Va3^eaLg^ zZR*?lmOp=a3Gn|0@1J_5h_&OQJo3c?&b1LEOxJb-c7Q!E#@dbAM`94Wg<*&hO8bo` zgoBNp9qTb<$+$BKVUJXpmw5jB7&qQBis0U;wU6izS1@z8320)xU+ zR#&mMqPnmex14L9AZ?kq)Wp5Qpq(AMlU8n0T3UZ}(pbs8wdk_3Fw|XCq2AhF_2=?U z$4h_ncnODdSvZI$r}||S<#%`+R;?|+-KJ?bW@CfgZ^JB+}Rf*jSGEY_%-sCNxSvU^s1 z;B%vDD#A<{bjaYwY$_J}l5Fv-Iw;SYi>iNp4sn%LeEnID-A4ru%Kx4`dD2d78e4@G z4~%=;$R}XBk(Ou`6IuVN|HHDRT~uhUHKp2Jbgx{CtQ4P)0`lSMh7ZU?(D39^j_Ldj zCx(5&JjCSiiaL*WV{u`37hi^5n$O8r5M8_!(QRhh-JZ`2m7XM8q> z0Dg1O8vMdsvJdJ@Rn*i8$MQ@0mid>_!epU}cTjT_J5g|OkEhT8*)O$3(cJTHMgJiN z8m_P3bwX?s)Z1!TGQq=>wtZL7?6XXl>=q=@D z>>?t1ciMVF2SJ^73+-AWty-JLYuMThwBu9j6y%R1V?P(huseqJx74>yF57=>aGfMn zRNV(YzpeGxZux%N+wLw3!IcJR8h+`5w-X`_->>?-lA_{e{W-xy&wZFh{Up!(lE1zh z`v+_5eg#p0aTiGOg{OK3_rHx*OlW&ix!x%yLk+7t^ zLJtJ7QvuWM`n;cl7K0}pw!?oC$8U8+7lHDhjjF%*fR;R-la)$YcT2|QJsP1QcZ>ey zgff0&;DF#+@gmIc8BM~RF3v)ghNN~Oc}^x~n?6o(kv+sf1t8a;-BzpOjAY`PN7Fj> zXuZCNlq5Pg(r~=+=%?Q1=xvcFuC;q$N$4hWazS!UMFAw^08myR!0hW&)W9yfZdJFc^6B3bWDotVG{q?Iz z)k_1#k4)uV*q1~h4CQ~;q-de_|t6ke_eqN=sW=Z$n^^v+?(OB>X zN!+JSnI1xH861Diz=INQumy6&6srws##udAvnH98F7-*m=?T)zb8VR)<%<4V!}5bZ zTD!%~`;syL8c+GWimosHXY>c?za3Tmixp8ca&|Fu{{MR5g~bZ}8)v)Nk7Y{>jQgL? zNSl!Vhq8B!(j?llMY9r>wpnT0&Pv<1RcYIqU)r{9+qQpg+xD&7r|%oL&*|6iynitw z;?Ev2=UQ{ky<;t%Tylsof3lEEIn6oHxU*d?S0H$jq87(pnEPP(>+k=VL^8xKgkc~Q zGJRy?u%!|PSwF!XxiHu@=NY$DtG#qb3ppP-m1vhp3%{WwjYvp35lJI#Zk(%jP=>^} zI~Yd4YCeC-syN5t99VFUEODlQL&tJVL-45@Ux5E`li8)~imtcggr2oO*=U5p#P7M@1)t|9t$9)eowi%OJ}me92Jo)X>Pu zAzX)`=F&8PA=dZ`B!;6v@r&PVH))>>tdgt?dxC#u_o?;>1xOft6yK&s*iBjc8E7_P zAbn?yPh{A1ad0ttcz=D}q4wab5Hl0ai3utRKocSvU_2N=JhKBhuAnDNK%*F=6=D}==Z;lAt12QJ5Vx@E5d8}?D;o^BC zNh>e2Bf(TYtvL%z#MkINkg^>t+Y{4Sw2pt2nU`qL>d>A#nCJkcan6j0$Y`rzqPsGz zSCLEOW)y^1&9}Fj5P!raPaNfSSb0H34a1}#$AL}N4pm6mb%ur!kOKGtziEy!3Y5Ku zN64AV#ImCp(|_2h@;2=H!+ST_bKmO!tjBTSo!F-kOrBq)kKjOZtFrp@R;5Dli?e^c z)Uzeb!heO3q%J=oi1G$^5#^JGG$r%5l18B>zlAsq1z^{wtX9LIjz{Kl3}DY#^e3#h z9v$|u%KD>=i;ev7`nWnDRKofbk*0IYP-@ za5N>@R8kmce4$w;@Tp0mnj_er+KPX(5$#$)+N)Zn;n&o&2ceHF2rX;Nw-du|eNJAu z_X*9zxv6)znQB8>fTSbcjBKp4Lqm1`Dmsi;AQcAGb^aC4rM6jdt@_Cd_1P*;hb0~^ z@i{*3>ON8x&{EhN+$cs>JR9~SZYTZ*vn(iI&m-j3jp;)B4Qy87*R>fJkS>3GY!Si8 z(`<_ikohOj)}vfXQ`eWE@O+lA%8VJZo+V+(=?Re^QwaSQQ7*2 z5e^C65D=qE$B4M<*EN`ObY7)i=Dr|)(00WLk<1Rj?*ubW=}g=;OnsS{ zxLCj6FE{@JGO=6tjgkj3k8p}-uF5yV4;2SdtZ|e&PthMy>Tu5t|1*ln+AlX6&r(k6 zw%Ax1cCIGdYPqsl=#>oySilu!|I!A;*1A65G78#{mLqW&!%(e1N^A1&r_-v-By>Qz zjB9a9z(q|2P>?TJld0&$|QiYxA&zP#sk z9$O#MMF3y5b5vhRnzZOF7K_PIB{UH>^u#R{r3Y(hnC{$&{3j{KCl+IqB@HKvu-LdG zxCVphvqLJ!*FmqQ+_ih5e2Ye3V|ulUR=2l#v5s1XYP@~8!-jvR{=75JMT@_+**p_y zV1~+?X{eqAMIQ%$r%xx8(TBs-{b|V(W`n_H!#FZLwb*OC*$RbIWpiDL3u7qWH$iKr zpQd-K_-T~FKDEj;f!ED)av#a3447@g=DFyCvh#-d#mVyKW0qp}>&v3e%I@_3aPcev zSND$5rTJ1&$CrN*>OrgM6t+GhTR}0>rne1YZ{DBYVAHdBFfQM}UE94`>rIA1WrX?q6ARz942l4{O zCN}oQ|8IYJ$$ni4;VY{0prvk(vq&5pf9GehxM!$?Mf!@lWSmYYHu}cT4EuQ0#gf{> zDC+qx;OHLB1k4l^#+k2xU0hcbj3+T`gJqcm`pn(w){E8p_se3Q^3VHhU2hQj;9y1> z!&^{TNk%};NNhZTzPbona(|0|InJ-JNvPp6xpse2!6Bz8Y=Vu07Lt8d-Z%zdr>Kqy za#nUI92d;DOq&RIPk=tP&1$05ph|POyGS`!DoeSF;^bwA{+Ww%d(}F}oDQp0e`}wu z!<_D?6Rc{-W?pjono-PBg;nNm0)i#;ALGrg;-qro&?6e@O6#&Ff+y)*;vGhq$}@Nx z&2E4F6{q4AayBbQ@@8qp=H;?KomKH`d2}PLs)8uyYFu)c0y3u5Fd#|lj!Nqk+|DrL z2QPv8GnyVYk7N2$IMOg1;5xib;_ zD=>~Q_BuBVW5QMXm&i?p1mIjwq@b@*GX_Ka^)q%`hGTfqiudlkz|vn5_L;y)EExtq zUX*TtvYDt~-P>%gQkIbGB~7w;F2s5{A|RD?8HnLsY7A7dST-H2_!b{b@G>mYB3OSM zh_y*NQ<8maLvP;(m<12;)u9hX+m38KD}M{CyDnhRz5*J&#eVm1!Ut+oqM?6l+MR&K z9NQm;{yD$&h2u)$`m!h%J<6^UC5GboaCa;4OQ?U%QRnFWN@@ z0fhOH!&m{A{XyN-XWxdJA5Tzq(G-6Mhe!hTztusov_r_=Wp(a89~#3Z=cxp1SCt{E zCd##!DOFbtO)8{i|AjNT;C+gBX0Q74-k;s@)a~oj`rTb3i91Ybu)qZ?KR!r^DBod@ zD)f87p1wo6U9$mxu7Ws@k03^@$Qkj&@99T};J&!s(9!HL8iEP>4eco4G~$1OL8585 zsw62cVizRCM&lD2$BVh#BSvBrWnFk%WKR?{E1&O?$rXqWaZG|}?mFdhfUHZ@5#F9c zeBIXnA<@(WgIrivqI*w?B88S$)CfI*?g@dWP_#f*!-mHJ1W9h2C|OSulqp<3sUJgA zBurZ*O1rShb}^`H$8RmT$^d^j9#p-e$PD#&injt}!N?JL6+xu=9|aBStzn#dS`ULpU+Aml)|0iCz-MzE z;gBx9q&{eTnfn&vw*%_91-NMl#aINP?2ezaV5WB9dD&BYh7`VZW?z2_ao_*si8fG{ zYe@f1q}QN8KqCK6R1ws7Fc!CVFt&CucQki4R&=yCw>JIz+W+41{C^Ip6)OHmV@V#D z_gcATh4J7p!VqOMel%BxqQDYqskDXYg23lo+_N3$DfA|&IW}0vd9Wo(J1z4~3&IH~zoxKubVccr^q?sU4On)A( zEY<8-6;U<>_+5XE6px9eWBzG23By6L9qq-^0zbEfEZi=|kun(j`Tlm|^S$bD5sKfp zARGn}p1(;bRdj7cX7=>)TXH^|-m5pAfzIdyMp_L+c-ng9BghWb$3b0fh>VxR)1Cuq zSnHCf!lk2D?tsN6?g1>>?V1=!DrX`#BFMbf#4*Xx&G>&2H1)Ezd1Mkd-Rmi(J~gG( z65#>WjZ@gKt2g%-g9S~gl=`GK7L#2uW7XZ%9wv@fztd5wG%4rd-kjl;57Fhf?=>S% zcw?gMNafu%=<9xW2s!DcgdX^jLR>IT<-(7)6~9zaTUHCWzgplKlX~D~T`giCS1a_m zdF6`IC3SyL8iX$L^PlWu&kKdAnV)}^A0iMC&%Xm$NoyMy>whS}f80@Y)OR%g2Tmu| zPF;|dQNBA_Cky6(NXAkM+XbZ@_;8j$hbZ+0Qze;tQ;R&0i0apSOQDT%I3Ed_L|Un}IZLgCg8FNb-NU zNVbTIP_QN-3+)>d(b?%Bb(}=Bi3>3L|9}KU2(M7*18cq{gz41)lTwRd#mR?zyv`@> zjFk-8f)X(rtWX+aU+t&A8*fB_W8C&a;2DfOL2dX4ySWMV<$Hc&=<@0Kk=1DT#Y?>< z1Qvgja1-UI30CQ+c2-%Xh(k^z8eM`ycsZvPS-TW456|NICDD${G!a|T#Lj8%QREl) zW8o}J2qo*Ev6(mJuZNS(=~5s(kw8#$!j{SkuAtuH)BuH&5ihW>Iyga2BR5tH z(1C!ag`ac&$X3Q(QZR3-tFx$Qt!OOJCR%@-GiImVOnk-?S9F+XWzgY1bT9nQoSQI` z)N{eE*ECN7tdrX2cms5(u1fc!Qd4hXy?}G!sQ8yM2g!L(dn4Pt%9|L3W6Ef$stVtj z*LSXgFzmr$?3Ev}=CK&ODoNeG@AjZjNJ*wglUA+C<^$SJp|mu!ueSiH0M4Rfw4T^n1y@_XAf4qSf}tNjnLO3l56QbXU z6Bb|4i3wyi#Y%uYnD&WM4Y~zNM-&Uv7D7uwNpm~waIV(feUKEKMsJ6|?ULIsyUW@s zA$hR(+oHf`l#WOjl#cLCD7YOSuxkh6wYGVRI_Cw%&86bW4}4y^8v@O5MBc0e zr*172&pxF1w~+gikHhu zV2{fx<`=Cd8!fS32B9@_tg$L5uW=S^&0!TQz*)|^lhIR%&)bX8P+*gRK@47wS^u5Yr#o;lBTcV8> z$lv^*b(OsYzo2VKvCE|#QW5GyVkdXc#0OG0NPo`x9^H8O3#Dy2J9`OwYt+mmhi>Ha z6V#SlQSmfP?J%b^g?fKfPl48x{sg;!(SULHSmmFvFIzsEhT9w7XSJ z|4u&jM#~Bqn|RFlu;5G{9FkY|cN#FeBG^+qQIuRLV#VgV1|lhz#SEwyWymIpj(4CBQy-SwoYvtz zo-raZsej#bPzo268${328#G0M(7D`&O7wkjge+*Bn-Yuty+*dW5{N0|Zy!PUgQyoC zEL+FyU>%`Xrp888o{e95JN6jOkvJUM=TKcl!J^Hx4(xv+3#;F4OJDHh_Yl~zsefIK zt=!P)`H3|SS-bcJH>Vc)4G-GmK-!S)Y_HGv-6}O+amLtgW_oY=rG5}Z3cy@0ytU76{;*?D_#}WL!%8el*ov#95bT|=rEC?OP$%9(qAqy5l9u! zFI%Cc)3EvjJ2|mesx#hpA>U=TGcwzq6J3k~;#Yt8uwQHgz4ZjI|1?gLc%Un<@UOOe z`1x;EjMC-~4u93$KawTCz3KmzDKitTWas5jhQD2~HsKCZpY?(X&m zNg02PivqRhj2j^nrzCgM5QSn}T#bkevrE`dF`mhwnYI_gX~qEPzT8RDYAaT`Z!lp- z6bq!({c%`k(FU}p5n&Gk4xHAht=Cj)&zRG-7A%tJLApnYVo1X5mywcOvUB4u!DBQH zLzFW)519&JR5d1H6}$GZSu-=LIMO$C_UC^T++8v5w=@#2MjXbSXl5Z|#_JGO8fUn| ztFn|N+D7@TQwqfCT14gR8eKfo(XD8)29;&w))lNX3C4^C4_yvO`*Vok@R!>0+kU^F zdygSfMXkUma?)>uZ`xnu9_jfu5OGT3g>o)tO;!#v4Br%0hdw`1ylW!9$|)o>_8))P zX<p9*L8ZoM36cDHrNIpIa@BO~My)KO;08$1nHrs3CKfzn z@reJyA~p{xft($sHvY**m+21AA=OdpwgAm|=>N&Df*&1)kTAK`wPI z@a_%qJUVO)+|O!FKR_W)Mj>!#91*Kuis1uK7#stYfl zuz&u2IiJY&?hpEYwja?$-&4r1*n%d27iY|Dwt}}Zm1~rSi|Wj3IcL)mSG5n&olUq- zEll%+957=i!C-g6dj3Cpnb_^M5GSx|jgfy8P)Kh|uKlLyw5325M$N?s6Q;Dm9tNEss9J&xlHG-N z-Tgrf6(ZpY^eZSudNYoapB9yFD>JqJIKtHAF9W*1f#e1{*^x#{XXq(imX+KiN=FQ} zf}$Y7;we$2sK`q~3|08$dz}D*yUghuRf*K=Mk7f$@^lMe)hrYKvo(KEvaPn0YiGYE zkOUVz%v)Pi@lN2h-g9D3jK`ZgxM1M#dvEk!Z=nQ@7E9&p#7|euSjvlBhst5^U=nZw z*eJEs=AJSUQ)va?Mw?7Ip2pzNyEIn!@dg{6)>#Mt1hiL)w*>W2J9MU=Xe&y8HT{Vr z>F|Q-m+N{6>plwl*?fN9~AqURpvBngEzjnV*SP#IiO}>+Jw&s>#)q=tGXfIQXSj zblgbQnm{g7HJ5p(=+`7`mTj7(aN-?%cb52R6jN}RJsrueC<1>*eM&3++hGiQ9kSA` zrtK!|;oCAGu{_VPsu%G(Zb7SWM8-eHjg;F;+ZIZ9H|V97CnqOoXz+N3IunrFA9b&; z?JVlI3Z3n%Ea_?Ea;FPLe$65LJoE%9O~`P)3q;H%zyj*Wtdj_Ym`|G{bMAF~LM8CB zN8gqGQlC)|tEYcfV)Z8;*AMYD51Yl*m>O4opE?R#sV@`bH#)_1e5=Djh`aR1a z9Kv47L~Nh09lr8Uu3-wv&P4lP5|sTV0o%V%f`2aP6P5lW{Ci(5&B}A)R6}+7!fZkX zF#bjegP@(IR_`$a3`83<*B4lbuOs>Z_yXy?e-MT4)_)I`=tdql!OE~w&}vVQdrdi9 z@f<&Re?EV{Li-9QkQmVue}s^eWtmCQORtxzC5iOy&R!YK_@#LGMwHZ%J9Q8T-$qi*$Q#kOk-{ z`Wl+cGkj!QwUc{77z7Q_@KQm+Z5Hts9lg{x=;Vwh6L!;8v+Hcx_nP!L8_lC^A=@5w zt@wX}!lZet(|+M77vEZ}cb4M#h{5lnpIM-ngg*%kVdyYO_sfRt_F)RY@jkoZ(43?6TQtwj!uE&# zHJ0f==pntrLBI;FVEzEAwLOwzg7gb`x{ZHlvjp0RCwUV)IVj6#G z!(kLvZ*@k0_Rp-?;Wg)G3eXDjAJHfmu~mKxU^1G2|-UyU4?!?J@tWlJxC)m z=`&LQIv|W=i3=^^t?=*?5$rjXQA`;k(vaP=^PSE|5Cdx#myw@jBv)8qKYbW48dV(N zUNw&!$N1+dZU-iUDaAj4fbv29rMG|8|HOxX$p?vX+Y;FPC_~-#>h*0!Sl`TxO-m&| z@==y0Y$OWgKnjC$g2ZS$(uw;y>rxX}Ltmi0aH>J+-hjMH=9tobLxPyw(YI6Uw_kCm zGCn_#chP-`76edg5c`Hjd?$gF1#Z>MjrowF9y{TBZHm0a)hsD!WP<~$n2&$+(rryz z6Q6|U5Jg(=*-zIc6A_c9swyQO;q@8K*HqNLl-{ZunJ0yeQjNxkRsQUev*u-wlq8pw zq{96ac-GFA*S^Fo;8zo42_DKC(|jKs_|XJ$FZ+hG^h}zcEzqyTWWgS@h9q-VNo7Zr zCv?L8M>F4XBPFc`LGoSNC|ZB1Ya+Y@QB{X*ZVYHWaP!URCK#X@;Nl@x@)@1p-Y}ng zI$AwuRd6@quroxZ|8o$CD=`1``8PSA&aC>g#FJ~^^Sl-iXP8>3lgT&U7A`oRi32j(DcKP_Z6plDE%1$-soIzmCRJGsXo2)+ODET_(~v5oz=rZxN&%8OU98{Yt-Me1W>eX{94A7aQGdKNDK8w{Jk z{>@#(e|-FZc%1(y5Pr)b^CNscxgU<%C1`*O$s>k0h>pt-WaDF5NJT)9lK=eHtV*_b z?Xt^o_m z&V+3C6i?=zx8MGaRb89an+j^Ca#Z|_`WR}?UZ%#yU8jLIFGYXHAyR&molbad0ydb7 zuH%sy5z47Gkb-B>+b(@!!}%k*iWVFY7AiCc%G0%$(~Xq;ol~D>Vz+6*vB*ab!0Y|j}a2UF2B^MWO<5ld#`3eMM zf+Gxj(@?{c{ic6QA^9q9KFQt0503WPT3aug6-cY&Rw@Af27sPO07mQg&~LN=0d8h` zUl<0uL>OwlSp5T^jbg4URVM+E=o+IP&LIp84&B}qz%Wdq!>!54N5AgohV#Ws!s&=b zo(KGC)v@sVBl(d&PN%~@4ImdTo~EOiY8o4MxEg6w2F`z~NM&+i?$%m}US^t3T2?jJ zS%JL&m{05mv`_1;6T(t_je;VIaiu@uay%G-h6)v}xN&}~NjxM(##3YM}lP-uT z`4FLUO}r}uUd^Dz6IkWKjoN11ux!I&6Y3W#C&02E1B7V9#fup!v;>v1nkb!Gi=Ggf zAR}q;>x==59D1T)J>qKl%P{5Yl0KsUSL~be+tq(1{KdX2*#9pz{NMZgnM%|D+u#2( zab6EHhDOb$)c9R7qhLgVADDn3h8#z#EcHk1fi*!|UCVj&D(R)VXEZ%or{i~C=xn)9 zJVV!N6**ev?Tj1WQTlV&vy(};r^h#9521?VgP}+;Ejp~*U;Hagj2^}U+*+lV7HV>G z2V#G;nzgw$^yKb>w0}14U24JzvaB~|HAOv9f48nUkTYrBWA&|~cG2v!mjb|@dtz2O z;H;)AgS#$SskUZoZ-h@?VMG?4Xu9`8@M|ENRqj8ERv!kt3{zo;Ss^`$H@sFaQg1-U zr?quUCvsTwLei!VEQ0rhQlf0rO~BQ!rci&%9EZZo5{chwdk&unLlJOXy~Ws{?nHmD zD)FF1s>%-7abG45ad`x*5K+6BLMg+BBeopZ6xJnVbnEDid_t$?c3ZP)BQ_Plo%szhcH%O_rySa_{`X%_0P+Pc8)U3uE3WKUF!cw!<2)=(0 zoOK~0^B~mi7x>8=Va#>ko2Y$GLpv6#iBbb%&le0*&OT*lNbIPaG4X_^0oH3q)l6zR zF2w=*zB;8)v_XYtF@*8dMCmD=Kt7^y!w8k)sRvMV2iUkk)_Q@|@pl8ke1f^$HOkz< zd0l=*T|s4*6Q5X>C98@j`Dd6&9hrYT@mCZ`f8IeB&@X^P33KDEDD#9OhfnK&WcA?7 z5{|P--SZx_Mj`#`L6Q{?OyZI@6qsudT1OnE*&v8UZY7D>rCppL4E8uEJ@AtuivMa2EL{)AgJk z0RIO1Cf{$RN#DfhPir_9?Y@7{yv|tv`ntbI_XTONGLo;46{LwCW<7H?&>t2Ei1FAJ zr*jt{VNM$ct;deg>iaVtf3^E+v{MFeu;WlP2My@sLmQjFWi}Tn))6D`K&Nz6=0idx z3%@RKR9;5cKe-!srjs)?tr!?_$;`dx*wkZ0v@hTyc6wBD3zka@p#y(OCPKyais<-_ zw1nNsbSuu@6=PWZi$XSvYZ6&t3z`leGj~Yi5GJYyX#^!bh%`6jMb$BT!kn@Aa?j4B zMO}~}CfKh7;oW_UTk@JBhGGPpqpn7B#hm)cZE052%F|n-WGM#c|vHZGp{% z-ik*W5$z7N{Fc)4s#$*%O+)x1*HS;brg_4Wd#(Et2X<;hUx;1J`!aBCZ?}qBGRogH zCB)3Ntb6N>@Mry4w+h@_o!$wmuWbNPFiq{`)tB#WBrXUmp97~j3Cao`DNs|n@2G@a zR4An-avYp0W?Gx_sAur&Bi5vbRDJAkq_maxAQlAVs(l$LPxOEE46==OrR6Zgu4<}v z35xu{x&n%8S?KI~mLkJRlw<|>x@7k{SKnYeb(SH%KIx|Pk(+d@iWF8=%YD{$8d$o+ zf0hrnlWMS=e=%hN4g|#a?_f&V+TwrMeCkvlS3s6W;Bko)@1y?>fp8lXLM{#wm<$hE z8#6aY2~{beUekY0tZ3)RC@4)jM)yKTTM#Ci2+h}%h^-6FX1#Gjr{*kMBoHV^K)d0% zz1}>1+Er{rdhOHg1+E9x%rX$OI0L9<)rhPgtxeqt5RX(S%5T9MWckr}AwC_l2l)L? zbeaU~k_ZC)2C~k&K_ZznE~2jIxoQ{Qnnwkl6g#eIVm*IKkyxOu#%h%xhFujf4H;OV zi6TbL7f)w273a**eAZf|U6B9u@k2%LguZ@Sv1Ff2%xJ4Y+J?VJ4A0737e>;EOc(`?~0;6U0f^^|B#l=d(t~=FGQuBztY{6XRl(@qM$Ql>yf>kO5sfDMI z%xZd4udJoY&Z19>&i#eBPv6)QXu3G7qGb^nKo+>jqLLGp&1Qqi?P-*VW8YI0m*>RT zK~WTakFzayCO21c7~yAL$~;4~ru$~w@(CVrBYu+iHhE4qXpvD2JB z466qyFm9b(OZ#D~y|f1E+B1uLIc!N(!T`P(*!qbirhq@VKsjji$TKs^9U?h5uN>-H zq8U2rb~56-9AkbvgmGOvgz1oTuJyd1Kk+KD(r?c|N?v~C>{*ho997Rj3eIsQ$eCy+ z-|l}s|2|zi@j+cXCW5f6!PXqdByLlR-Q`A3#HbJ(-fA5lSXZzqL;T-Izy)cmf(MfX zH_x0k8X&s(#$`L|cVl)tzj)czrAs!&28F*x{Igwt*Z=0wBFtJPXmmzc6HLf(?>Zv~a`>Y?Xv;Cf0ig1mVu*O* zyr&FezNKXTWr8gX{z?$mfE zuAh5~-7JeQsKQxM+IjCJx|0}Xn7Jk*Z^kH2vB2FCO9gVnj-Gv)#u223P6<26-6*xm zc4e5=?4%{6IiMK@2U!(qp%Sv;*9o57bnz=YC8Y6A3u3jT#b+s0ip;5Hqs)J}_r`5e z8XVH{Q!@oZ!v^mB5>5-HiC7f#1zN-QBsW&62gy+G!jy{=iMnNrv`oqlZM5|VFf3`6 zqZytRNm$((rK4Ib8O2Q%kK>+&&%oW(O5Nz@b!wtaVB<(Qhhk^0pkoy#nAM{S-PDTC zG|RQhDkZb(-W-~4$2i(&#)W^h$77V;%yP(C!OUlM8cQ#S+>I?N@uYt8zd}=Ljd%69 zt%tz&Ra{5}X2lc<(ZU+&!mm!ChWta#q~e|YN5}o`>Uojah;HQw9Ya&4iOFkP=U=DL zrP%rEc#t+RYtffM4egkg5+F#Wq?a6q9b{{o`03Y--+=k$1K}TAL8BGh+Sc=h2`92vZuR3{RoL)y@v>IWVx__w zGPn$PT_uRHsPf`NIDvx`15VV1HPAV=GO8VP?2Tc`bI8rkc!C)fm*IT*PL8{U#&RyS z3oKWRyjCnmQ(?^EfjYb?O~U7DVWrbB9aFOy0xYOJkEZwLO^<)Nz?&;ROGq>%uGllE zt*O4WqFFK$)HKNw%hJZY2{gQtMH-Iepc&?)fP>Z`M4ZaGF=cdSl`us!PDFY7ULg$L zhO1v;<+eiaDMmyF8|64*kBO)iV0yCoO3Q@r$*IK8p!8vF0WGi_2q=ryDU=#DmR7l%KPwmg zoCBjE6apc(z~nmUm|`jBBmtnULDklf8j~Lu*w!%Zp_rR-nWMUyyfRJ2dHzu7a7c*L zpc$l0`~W6KQ#2TG1?G!cqw+8qjRbZ+Jj-LZpL4PWRpo!-Kdi_Qcl$z2ejl%xRV+r3 zI1FVek2wuau%1K2F@c$nRYDCWcD1fg;^hsH^xCqqJ*LG9Nqy?*KMSYEa7?DDXw6;) zh6$^}rrLJkJFBs0QtVY6{FPfKeM}^ZsSyn|lt|9;!w$KuDpfurtev@!iAZvz8U0(787u&=p!|CYXkA^S;s%pJ=hoOGB&YZ}kvRhYmxEnCnY|CHR>jR`_=K_&~a-29B$q;qc92@3Za{?4=s0Cn1|-l$riQo zWXBP$i@I2&5(nnTn*t4qk4R&IYa#6m+b+|Nnn~^37m}=tx>}nO*oeJE!mEqtPXo6j z4ySDOO^O7h{^GA=^dR<6gq?C;Yw^zLUl>>D{ ztVZReT`Cz(_6ww@96QHxyB|P_GAcGmGyMxo80826A~b++R=}n{;3r(EBR~b=q$-iQ z!%RLf{)}-dt0Woma0@p-6^U#TJU*=bojzw5G60KCZJAe(!(PLn>-f%VAn zo{D$@mRlAqok-`-XBb;CGW&+Y2N8uKb1|EEh6dJXGNynD1t%(_mQ0xNN3s&Y~UnWliOTuLzL*P@|J()=u>i!-NJIVDd8tfXjE;HBFOLa z_uaZ*jQN|5Vjhjfp!P@JWD$84>)gH=Zsf-l7G9T+E|0eN9r?vT#gPvrZVC0hB)1DJ zL2%ZhhfuQX#7x-6>%8uH7gO}8#qPN)pcoTi+fq0{|B5>qN4ULVOb{;zdqd{>wiC%! z_SCKZ*bz&*;v+ey#HDO4#2e(pmQ1msGU`K-N6|tCX2j^@cd36`ZwWo>BuYZxw=sL7 z;|X8Qi3wwYbLQq{wZ4P}w*7f)8Ra2+c`Ba48yeE?Q+(c-lbQXTetxj6el_FxMFT{}LBD^27w!i~ zIKcKCTa?1G-63C|K_|z(Wf8TRp|Zj3rcp4d#6?rCx`WsV0zv1@$1k|2W-*w@-C<$> zSg)9fwZT7KtnEJT1XMQjJO`n?+$ z#cHaFs;aTD=`AB%8~c&^rhwDX;cj|t-FSUT#clhy*!Q^1WWv2O zfjAIv5eA`#VQY0tJjaAVcU~;O{e2}IymQq$m7x|R{0YzZsZvmHblaoaE3hS9V)N~Z z2ZW~dMnz~h_Q|ZkX#Q5uqdHg3QMY@S8inR3gHpBIqT%Z_k7JEFvEx#4SsCC$7bWt| zL?2;K-9B~&0wB9N5t_b>b zlN`(UK(Z5fCEDXX=QwA3RfYn6cYP)MyN>xv8?n}2ugG5-vj#d$Qcn_T6V+`@0Ve_WMc^g|Mo!Q5>`A3Tr-M2#LIr2 zyICxy@&wy9pWR%&+{*_br4b1H{Ybu_S-{sQFf8jPUVATNC$sh>M{i^>Hlyt#zJQ=8 z%F~3b)jwP$dqqtxp>p4;=x@z^6PDJ(sw+(B$b`^!mA3A>Weu*y0=r?-7fH);3A0?r zT<&U@+K1eh2JLCDrC<~KWW|p{?g>V4%f-V+H4)0+Kk3LG#WgDdRj@U+{03(v>unVk zZwxQ5yWG-43&pF_lkk|bsHQyWXXvtdUBqlxQYRaS$xK`R0|?{w$2hAOesz1=G9=y1E1}9Tjb@;d%rG;e z)RSiOLZ)Vx%ne(O1UnT)i+nMAChF8C_62$e%iB0{nEIY3jFr z?^k$0c98E$WD})^K^B%_pEoUj=a&y@H@y8^e8_jQ*d&Qe1QGVMFW?(02^)p$iCc8 zGs$`1;zWs#Px_1Bqxb=%`t>E^i0Si*X6>waiO-CH$pj|E1HMQb`?OuQ#aOv9N6tky z7VUPv`-LUPNc#E?w5zFV5bBYG|58oy88E@ESvO@>t->GLLfO~Yx)HO8<9 zXD@!f&Lc9-tE) z*DJEw+uw&A(r}l+ij){D-WpUrQ%#W(^wmpA%8y&*LtPuNU{yc}2<=iaB{{|;xwW-j zK*>a)OKc=22wl{SQe7bT%#dC|ppW^e91|=+$9Mmn%4V)!cll;LUSl7wer=7262p}} z|Mh{^)i_++Hr4)zI*6{&=vj{zsKvt*nD3}U8A|lbYWRTkKpidFXBQcTwEr|?RFd5n z=|bPah?kr%M;PdJzWmR8bzHfv>+t9YA{nizUmwP?va;(U?2dDkf zt>`ojD*ENb!LHDnu;IJn^|(L66F>L>nD}io+=Pe|I$phH>#=Z)y7n6r(NlciTnmmf zO9K0NM&z?XtlPmKUT_6rw-ujBE2S8-@&*-(jZFB(pLBrKyF3(EvYeK8=J$gz72dwI zD2fe5YG2+Ss68VbWKwe@>jBR&U2T4~v7>1Zo1BE5-PH}Vey`2OU;~eVY{Z!x;B9*T zK`?t5^Ml9e^XM6fiqZ}D^#gwIx$NpIRYN!Liv{fo+p(v*n+|ta8xyMIN;E@pEzl=b zY2hlMsG4G2w7k5&iRzA?DQ^ov)k}Nt-i>P z58;YC@BQoPdoXQ5M_JnS7-VOHOz!|CFa8TB2_~M5-=oD*k}Uo*nIofowO0MFTGn9` zMydT{vnZ~~!<~eIT1ktv30ql36XLy)aEges!*C`1YFa3Cn;pOde4JpS0ADfPqTaK^ z9&fK4VtN-dOJG0WC-wDsKe+v_5@mr8$XaIyf(r$u(8jmSBp!8Tc2AScq<)xbQ?OB* z>WGqJPd0~si$UJ$CS@^liXN6OCxE5_Cp#+y+Jb&4e?mgw@((6jB zr%0p-CmQ+OH2k66($0=n;XfrRNSZ{fBlrX{+PMQYX6)Ek=KYf4!B`;36=n^pD?L+f z-_5CbDM?8WS0|IS050Vr!Vg|1``#0@@80KWDW~>riPx}=$D@$dy{<5a_30$I7-27nK>Cw$o&Z>dR*!bhufYx5=iS19LP+}E!+5#lE zw@JhrF-rwAO2_iR3rq>80zS7jX}b~LSao-IZ+FqQ1_Hcb0nP~wC~-9t_9Xt87=k{j zGH#DOO<(*~TeixwlBYQoXv=1j6hWd$$mpu7f2@qHZ6p!4xWkz=`5Y!T{RJ1(q4%vr z*?E2$U7u((|3))?y~Tslo0SjrCVtUVS;oSEdN>nP^vh)F9{BpLrC zP4}dqz}M}s3J@rsXEYc@w`c|V<==@n*U>xtQlV8qs4?WFYTWd8N-%FWn<+cOnDemEFwBG%!WjTL; zU6O*3insbpG-aZ1Ga5C~(SEQZs9wvzFA&Z?EV?K82>@H|j+QVX=M(cYT`*AxB8j&4 z3JT=MA_bJg5`x>PzZ;{f^ji1kP<&F1OSrhtS|;cjw0dJUV05;_?_es;_AurjP5WE> z?L^J0mveid!&_%wDC2kmOf9ydq z2g#|mtDNIYeyLSf6H^t@y-WH{!Q#S1#49Clu?CFm3ns~ zI!>Gi%a0!RF%~`He5^3oH|5-v9T)0x^^d*obOiMD48!rl&z!lS8ntG<9>zmGLNq~% zPOVYWrer?ndDFG|Q;o@gEMVZX7dk~H?$xq6WocGbTOrZ4=^Fx^maC{9ix@=0i!%vx zTs|{uGCmnv`RXT^uN8u2D@pVlRLixe8hRXX>$W9Q7d^WVo8SQ4aUQit;4TVOz z^#a7y1%ajPllUV?o7e;Cu>Ehfwv{LJm*uHX{x&+I2HMIt!*e63B6*YXPcuD2q*6Dp z_I^9@p}QWl-+)l3zM*9Pby|d=;Mr=R+nT(9nRKr@5eEEDq9;9NDpxDGMOqe0nEc~D zrC}>qyOGmLUDi3{lk4(zrYQnC+5qP06d54en*h&^rjCM`1 z8Og76WtOARcR{S`m>~Qg2bQykC%!0qwa*JyZf*hiUDIgwRvvDC#98gP;y1C-Z!811 z)Sqi88$d(NKkNWn-K$7n7D%Q}VgjH>Xyp+stE7TH+EaN_@pLOtPX!ieQwJAnndWZb zaW%|?LEUReO~8H1MtyA`l}*J%M>c@yaa04ZAI`3YK6PvYs%*`@sk7T{tI;NJ*DoJo zbYp6?f*p2um?b5Sti}efy>Xg9nHYiM`D)5;I_W?;v;JsxD*q(Bd3zNsfLDTIbMl4L zXB~?Vae7AvD6F!G#RDtvBCt$toyQvMOXAmKl2G!YE{@6hD~%{8(D>^=-*5p^GO4E# z=-#IM&PcK+oPXV!#kmian_tv`XwFyXcN5p*14JT|9O31Qd)`R0FirBX%0gZ~2t0n0 zp*UH)5Ht@{2Q0umv0&|4T{>Pvc^uw@%(A_m&|gwVC{8_YDR3wy3-*YJSc>W>6do63k|K^IaeNc5Qgi@*Y2bl8?l!i17__BEmxS>O1P*G1tD> zkL7$pWK+_IZrwRD-WMQWw0DJd%tdSpLyy73Y&wuPi>Ce@U?HQ|k8T3I#46ZtQqbKh z^PGBxQm`=Ddq^rx6$C7SO!hwzmpBTFfwZYR}Xu#DPH zd`qjU`l&tGO48Bj!UEkXiR<{~VP9B!6C@mhJUbP+sho;0nmN=k00VJ?CaJv?w_st&f{CTlCRe6tn#N|dR06!s@(t@DVoY4~bOC78EsTe)*sdhXR*W5BxL zd8{dP4%{yEH%aBXSWK%#tPn_vq25{dpZH-hhQ+9RySFsigvL}r+_k5nQ-bmar-WTo zfvRg3!gjA{jQa8_E#3l`(;fKn8`g1-KyoQ_Y?=+lv>aA#w*cmeN70_~DF?kk zWlz}|eR?eG<_dQT7TN~Hdi(9+C;lXfrn`=K`GZxwKg8@-(?Nd2{o6v@tAVlfQ+~nw zF^%~;T=Giq(Cjv#b&QkAY>h!JBSNS{Vb`=%4Z|d6=F0OnOO8IprKOHo55d?R<;;xS zLJhGuDo4j^ei<4h9ndl@iA;PW*SU&3@7hxtm~C9U;*`XZ~g7d>nj+Mm_F zOHi;uiWphLOY_#sq7`G?!MldQs+k}6Mn_vq$Xy?l$@G%il6Y7dUJfPGoB}5R-208} zr+BzVdo%!5x%drt60YVt-)3HWuQ#oT7Tga%)UUg$~$T=t!c9*|F!!{1z7PRE4U^_=H)i}s^Ms^TX#1dh0T5c!#h==3UN(hu0pGwWP6ob8ahuAd zv#bN()^y1DjW6%XU^ADA=<>#~&~1K8(oxf5P-{vRenvrn5pm%GN_?Uo%6Hp#VEnt7 zioJ7?s`9Zt;piq3w5hy+Mj}oSG`IR(V!siCWV(p(o?w)7NAH_TEkXqsDaasi?DU0dQFGMjXH} zezU7zKm2tU>_i-}Q+HB!ncsOq2BK=S(r%%9Hjqnn#rgXo93sAp;O4VLcV2_(jfZ8- zka|itsQIX6Y_}6km%Ep*l-C7+>uu$mBv~;RZ~n0BIBhYX?u{*N+=Yb)1MbBG?nCq) z3m$Gn4l<}*1&j2Hi*f2q&X}LA3 zteECStbUH!pQB-e;yK7T^?@P0tl;k#L`Pj$)1hpb7vWN4sBIG(BS+ALVZv7_KYrtdjZ;WEjF{Ke8$uXpvAb*>f+{j?nFI6WOEA$9^ z3hoTWavzyyxrNcAzl(A^Pw54_wa3K+mb3^EF z|5dc*XofjLr$#tTd^K(mZnvJ+Hm5wNXJC2W zHbPRmcQV?jN1)#f$ZS+JG(#<1@RA?J_+$-SlF=w&&T8sxG1A*>+n-i{D8j2AXKEl^%(*eCB`f_R5xb_L@f_n;z}R^=>e~t9o9%M& zAkxl*a(-ieu4eJoV3q$XKn$;|hg(0A6}*?IHralE#*FN7;%}|5LHb{Uk3u^67~9f< zF*lR7**(rG^$Y+`{T!mn8>P@PjT7O3R8pS4D7G$1d;w77tK1Iuh|L31g;kZFo7%6t zgQKD={cjibm`9Vzh{dflYpeDXD$_Kh9fOOydcc=iWP`*6PR8kY(6HYG`(m?^?5w&eGE`N9WwtOlSk>#$90b;s0Tu+Rc8QY@sXF*X+pZF-3B&6Nu-$k9&H#FZ(r|T+ zfS6iLbuHP7IM<{~A87C_QgOH>c)Ky^O->*dzaoGR;`B&DgGv0oV5^rhjtPeWRgQor z{=vv_D!GU%?|W7fmv3YT=s1YEf3FMlgQ{?03oRLDEIT+E5+YG2bYx6P7$BeaR9vZg z6o7T$t1c#RM*bbiu&+a_a)6`aD1uJ6l1Kl_lmL2QO{IMF`Ul#I?#WlQB$FK3Ia;qc z6ARE!b2(Qu@%~n`ALx8v5*>gnb)G7j<9|4&w`YDLXK!OQ(KODpq>c9~=E*{*UqH0Z z%p1vun)U9Gm=4-)JA<#FTtPd6rB3NgjUmvMkzIuXv4095|0cdY;>u@pG!wa}vSK*jux zQYV%y184@=huAj0%_{tNP(b@gDOgn(EeK4IwpX63jgI!qD7Uu|Uz#@fz%{gvY7V** z)KoYSbbrjZNB|fTqR#tw<$cZzbRhr|CZut4ada|wakDmeb@ze= z0tWl%nE%+RtHA?7R%gK3AZ5YXoS}S~B{HlTfiAMnCC0De03e}i-L05>{~kR4_xrE& z{PR-+Tnj;0RPp=;{O&0;lFZh zj6ID17wo^X{(C;5fczc4TQy)*{{R2iMf5KO@&5sW>|Y4-e~b9Su6lQaBtZ~xtG=^3~%yUK*K z`NuY5T@1m}LPGJvWS9-W0s##W{?AGL4?E%i!q)#6+koibVTb+``;TpOetC!biyf%& zOn-#}0=mHbuUPPI9_|0>?myQ2KV4464FpF1w`?BqCyRev{>P?nhyn9~WekB~rs9eN z>jR=2l9uC-89&;MK=9?&$P!G_t3KJ13AAZ4KPir>i;;_Ir2}IzeOgQe_`tA_0&iAW z^3&l8#swLaPID1%P3F#Jj^mhit>w@3MOS_V=h~)*(JsK;K_m0OO-%%sTmcuoGZ;=z};*EO)Vik+umGg z<4xZNT>o+9X-l6UYWHDvFXzRcTPpHp9gd9h&FFE~lV>%wKSy?vOkLp3i9EhFdssfj zT!37}{dLXG?8A!l1c^G=b!z%x!2@t+a40Q|AYMJ|(At`3)Xw5Z2?CY<1GkC5&Dp(Y zzti4+h7>L|OfA;op*3|nW+!d2ttV3`+d&}b<9O5Ty9bwj+I{4GD(9HCLyqOq%$&7- zS;w1yduhh!0+~Tpx$3#OU3adB_{LE*H{w4D;kx4%HYkY`d*uLus*zvApbvl}vVAtO zva$(PYP*($=%%*S;D0od3g^Cv<0W{&3&8r|`Ju2i?oU~Ij_~!2k$9t$chx^2XxVA&a%FUfQR89w+v&h&f2$)1p-rw+2PiJ^ z@CIiVv$dws{)9-6B5~t(5gV}8wD-iTH5Pa7Uf)#=rF>queQ&{lZsHv+|RLdRz-LR2oJkXD-lfyjDrH)s3n<2LWtQzpQ%H)8n1v;H8v zrwKElKt)ikKDNM&alSoUtdVLd0we;?TCGP?1xDP6wmt^5+<+MxfG;T>>DSQmogD#` zdv0P(V*mOB4^vFpPLg>dq3OK`rBTSIR>1dg4zXBD;+DO~@9V4_?trRB_Cq;4T`d1n zhKzuc(>Dez1L~dR9uz=#cIxO~mE}qi@c0;feE_G(`Emz7ByzjU=+0i>xyKUMnx7kh zkKIOtzW0Hc#}V)9pF5+8DpshW>>s%uR4Y5J`^h}Qhs@_3A+!afQs311 z!Gu29BbR%0EDAOv&6X-7F3k@CKt&voAVx&mqFHi`kyp=h(FU;hD+C|`rIGyrlfk@8 zSNhV^y!*+8m`?=xTMz6DvOWrqLHUO`)NXtR$jlE=^&Tx)pzBzR07gX{2X8hadNc0<1~^|GeR)dIy3` zL^e|xI?t>d(I+4!4GRU*7@FH32>yqa5r*PBcn9kV2umbreX>E=4?8NDAo^KNoJJe1 zv#@(2Q#SQJrgkK4BOr6w5v;dHFsC~8c}AwcVu!5a5jh^YUd{DQa;aF9-XE?U*w2SU z#%c^6|oAHjIlCPlsY4s;)^9(`NQ~OAmwh*lirygC|dLj)1V|4p^1Zb(8C( z;=b!7jMZpD^?gYTKhM@pkP1&x#t6UIO}6`waQuGyvfss+P{lm!VWxqHecxGdBpisF zOA!@D1b&cnDuI){4kua5ax&GHFGVQ<{ycQS>>1M!H}f>f70}g8+cg8*G-&^rF`cnd z6)>o(Wu#0TJj?6f z92+fw;Az?ViOP09xDFR9N-YW2sM_1l;afz(G|1KGYN1|8TAG#=5nYQve?_Oq`Sey$ zDrg$G^Aar5st&x&MP}oB!eHqKq+tL{yo{T=ZODM^EH6i$AslOqs?!|IB$>!=KM0Iu zM(d(y|8(A&a>(?`X*{Xy&#BEC3r%Zx1;HL&PDxVyH|Hn{Z`40~ptwjvrL100U}k-HmB`bRHQ-B~rgi5*H@F6?ldLmzte$8J|)! z6&OC;tw(B=cJmisyEZIXZSp;Ja>MP~%q2|6q6X|01^t<5C!*(S2PF+YBjTtoV^+T$ zLc|2_l1b7Ge#kL8h5L3ZPiiLVXQsbSLgmNzJ-&*lo)Kcbk!f4Wv@O*{asj@bBWNEQ zXjEXeRd9SMjzJ0r1aCQNq-8wAEZ^1!pSQJ>yIe7yioI&a{F>ZFY~ z&EI}`LIy{d!q&B&Kdi5!>eM$q$CvT08IDi;A6&E5YzSBn~r1T~Lk5#!3WZ2y5D1+aE}ASo$J*k>W{O76!Mt_HsTNNIPPV_7Eu?pjf>?6@j1~t$HbPg= zRy=9MjxRutS?GRH-JblZs2G-TeIFj5?5-i%Xpv{sP?dCyANFku*G1P=qBlo|RYS#vM z$4zl8Chghh;RPW0>Hv(q#z zVlm4wvjNYFu<;yxlJ^9U@tkabgf+{sF#&+slQ#8bnrs$@tVRyZhN9WcAJU^2I0#N< zn%Q$ZEHBWGDa{K{#JB?}V6UdXO;-IRhN!)@O}Zk;@0S1yL}-V?#}f%?HJ6*wv`?A^~Xm^)#jlmO-tLr>=iT?>)zsUoIR;5_)Jir`IkX*S%@31$F zRq(VzPN`Ra@mUK;S{fKEctYi#2vHZ_-j?aCi(0er%1N6>b9GL`>gJJBZG;XAX93q@ z?;UPZ-W`H{eAecZ*6){KLzaO5aq}rJIIKFKU+q`*uGKyJlmfOdOQ+%WxAabTlOiVX zcfzn_&!tAGbp1qG_@;%9yvGds%PZ{-L_`>?M zBf9#80O{KNv&=rmK67Fj*))BwA5A+MFuiyJ%y08opirINN#=e91pA6ercjM?5!8JA z4ddO|Q(`!tadYb5Df(=tpYaat_nKO)2F(07Ff373>%T$#8~V}z*TB#;jQ4*F3{C$P z4x0h~UA)=B_52?(;Xig*$KG*YBH@dG=sTa-s7^&ewAbtiBh3yY-rT!NUz>%DyAc!* zsc9%>iFtULLwciQ>*LwCqC#3Zkz}HXYp&f zN&PBGfz!s`FBIA##Wr*OLBpCIl9wX<_bW;fOY0b66K5I{ZMM0C1G`nl>U31Hv}-o% zY>2#ENyh7Z*$CN`Ra2U>o!bDKeZ`6Qs}}#N-zgwc&pYvOdnQ5wH|R(jIg##gf2hW8 zS7c!g0~;0WmL!OS%vHBq&kplpr!Ml`BxTiZO{1Rtu90MPI@)1IUZyc`HvXQ5H6k36 z27{+=TbJ4!&siri@ZP8)`ihTk%SMxw)`cD>_h<8-cYP3a`hBfM&H{c$gOl_h0;%bC zSyO=Qx;bIRaF$7MPP^}5#rRn%L)ZZceD$PH=kA_z%c69F1;r}Tg|YLe7_VZ28TZG) znOJqU<+5UPj9Bz!PDV6CNiNTR&Y9xkqt}D$cb8`kPBwH6uKi=VU^m{D9J)j_@8GE= zdxui?p5^27*G*To&vzTaGX*L;4r}|@(jy>mXYli@j<=2b8d0!q+#|;$y%_NQRyV1gOdA0}tn%nOsA~)ydm^+xM+Y1%qVJJpsTW z=aIz84DHFLtz}PY#!Vo+({RQZeJ34SXVR zp@aA;o)?81YqoiPUX)8uS$z(B7vMrf``P*F_hVj4;>k`TmjaTn;S2eM-X(y@Esoo~rao>N359$3FSN4T>64$)dUPCo?BI)+{who&JMYFv+N zyEAc%z`uFNqTPMon^D5NKhd+?>>QkJJx#o3Ja9U*To$o_TYK1g&wS}=IfWEOIA(`$^*hR90&O%}kDE4<@n(d|;N*V$7jT8cWEgUrEV+|9$k5Q@pD7 zwvv^ALPOO~g=qBs!LggqAHKj>pM^uOMmY9^rPd}!@)M&JhMb35mIIJ7)LNJH2#k)y zO6R?7gw$_$S3I*Ma~xR5`AsJTk27hiD2toC2KTi8x*MkwDiS(GrdStP6rPTG5hpui zRwITE^|zHSDUy?=kviX?osWqt={TnCKJ=Xt>W;F2J{O@qD)q7brcOizhB%H>sKz6G zo?jqY*;3<7_41uOn+hN+zn3d*D8qNqDdLmoM?`VF_+~tisU`T7@>?d^>x$D-I9w)O z8v;ghdW7T81b;T^C%2`R&bVm>kDrz`ZXPRT_Xp5sn?fOV2)4|nc1*L_Ha~#Kp?r1Q zNGji>{cyG|$YpG!#OxgF zg zR&5myJnoXGn@$!d4p<~DS!nY9$&LRW|8xC97ReJe?c=C1u6IiXNaj$bUW&(L1d z2mV6RLY;&_T|aF*u@`aN8P=rW8pi5hFm<3xvvO~r)44EI-!%RN^G{k~gB(hAjKo&WRN*P#(K);P~P$k{BeoWc<@-P%O8zCyL4d1|B%1 z)eewe9g>6kZYwb4|5K{kU)3M5u0aX-a&+QzX#RHfDZSolMgQi0{c2jWS)vxWErR7_ zJj-LqV`JY4pP2VY_Ttdt1GWS~7`G-+YIA)tlft|#?=xonL1z;B0!&nmfi}Gg%)a#P z2%cQwk{<pvh+u1&C zf+IY*(qEW54W+|$3b_N3-HxyTrZskz7^Gy4@^7p4At|;VqL9pL#+YSnL-$r6_8r1j z^v?>^w;4nhswD)-{^LZDw&QQq08+vAC~}U zFvs<;eA^oU-~(cfWF}Qoc5o|?Xdnw(%4-rySMKd6Y)%yUaJmOdz#7wfs{{6YT|RoV ztc?`8DPTPqcr9DsHD3wY!f058#|HTzcn8=6>703n`J)!Nir-h644Nr4v9B?nx8heM z(LN()(2UzU?$ZpQ{o;8f+xhvPlVXFR=B8be?aVDD6WLtrMr(F{=kVFIG@Nhq2Te3{cEy{}oU zp3CnW=dvp}5saTWBrlD$&M9a)Jl^uVYf7{AQ49VO3q=B%t#|S)K-ca`#s~ohE$~1d zELWbU;CGN>RdZ`lW7-fa9K(Cz-veQ41}2hN&@d&q}2%oZLJb$+ohm6TYHHsV?L}ie|t%9C$yvpw_9ne@OJ`#Mtn=94}S~ zWl>&h&GSexU(h-Rxw^G9%%3Ix+E$%*JJ*}S6#-?!7tHD5#kl7?C*Y5T^6C?G%I$AE z&VWFscAo24mW&&WPJ+#lvr!>~PbE=T8%0wCz(s^1;V(|sw#ZOJP_o4OH@dm$pqn_I z%tgFAtEbZ&4|XP;SFWi`z<2XdV;gNQv|f`%qj4NVwHXn;2vHiD>w+?^daD@{8$|j}i!XZrj1mQP<g2T}RonHZ=$>7HA(6;q<+KAbG>AR)(S zwam{s?Q(VP=z4VJCxU2d7E!Y#1V6VAVAM;JM{`k~XUd1Li0_zfmpQ*mU$|^8G3}tw zoYtMbyK+#k0_@{U`;MIqHOuoZIPyhnvc>Nt}h)J>eO#6Yl~#v6$!?b0*T?74w$ z`p)fErj`^e>gdUhIYTzKk+^jcARWY^C`K5EGw(<{O78rNxE;wUP%m4T(Ok}L z`SdG$D|-y~_MDs@-fWlE+3i@3+^Z05`YjQ2zbIy(MvMJi;YCgf-FqY%(rGlRmG(we z7g(s-AnR2o3UDYeJqc=I;+8B={kN}`L|ZSI-?|@Q^G#TKP7C)8th#jp;DoF?XII&a zu01nu)Q{hxv@AYb$CGMv`|~`B(`cRG2NWNFGJ@ct z@(?OFPo672blRb{2vlLG{4$CrqAcv0{Al)3z;Zh3u3gJw z?|1b8Ki1^YbjQ+nb0?54;Bb%gm}g~rM5BqcRg108?_i)|m3aGGaeZ}(&q|Pk2TYJg zVZQ5PV@1Ht=Q9-(3CQ7#y=aEe)5NNYzjEF3a|^XK0W>X}wCe1og|Fohlu@9TY92CR z#jZCTLr4ABV!69v#X8mGsjL;NUFTzGdi}IN7z4sx@jffh(3%fEfZPRt7hhn*vpTqO z5tTf=5;sc?K{74eP7{1!;qN~#NjRxRtgG~lz}LoIMU$k7xTQ7#+{Uov=0j&^ZUau_ z*iLlRe>z$IaMiJx@DR3A7`DeU1)+91w+*@sI);!YZPHYR4i;Z)jip@nT%jEHXr@%H z$b9Lk5XH^H@im#1mxdeR6^x^DcG8up1`#BwW@h}e<&##?BgS>W7TEkzNAaWcS~l)` zP8WRS+jv=kGq{Sw?B*uf-oPJ_`r8Z`1Hd~v__Vuy#@GhiT*5BYmiZfGrY+~uZN5h| zFcw)4c*+B}4h8>gD^t>aeDfzh;)8{(gA0g$1cNPTyS2?tRO^MZAD>SAkuM;tap9cf zHSMKg_P*H5|K$xGmPj<{K~WvLbUk8F;}AqFrh2c=&3%jsAHr75ndI}e_$YbF9l(+2 zha!!4ppe?M`(2@sgTlAoeVaifM2T5C`l6<>yVYE*w>d0C#G?)qQJEwnwnEgPHB%&gK_h$w7g&cM@o6keyx?m{S{B(;qz`uph zvATP=?zv+yzxk~|IKrFB^0C0s4X~Z*<}2#s*=(WBv4WgkJ12|Xo2!YfY+&|GSoSwV zyVk_{VUg1V2{HSe6E_mB_$S0+kBZ62FAG8`*DQW4}wE&Wx~u0@PR`6#SMpS!acPf!dKoQnmq&w}2!6s>Mvt z-=`pjJ6*@$)0{h__8%=cm;RB5VcaX*N7gOtf3tFv?>c(g+9c%%G?a)?Y$amD936EO z)!!~(A5#*HW}&oArZS=;EW(BV3c;NuEw?VL^JqokTvgq;g>ps_85>iO z+JO`83LrNScBIWZhN;-n6Xf;=JD9eL&&(y=K>^P&8wrH3wYX0i+h4!B9Ktr3T(s(_ z{PY$5#B3zYgk2XtvQI0djIC!oSp}v? zlcyQR=i)IEIbPw#{+)1}ukA%Z946T4GFJ{lH?V($Z#&3Ur+#zx#0JK}0!dO=Zf=t@ z_g4+1u1Pb`)z)C!!CRHhD6tB-C>=AxewVNFZ$&^BNlBF}&bRmuo){dr6zZ=|KrIdbfaK88vEK?j+pdK?u02QOm-++%Btu(0f3 zDI+8);VigW&4%Vw7pJxGJ7g%yUvM- zn{SS#^F318DSd|ZrL)+u%h+R7_&7ZWMTCV_$WaKehpFd6+Eys4n-k;#)W^^K=xC{A=#}h&OEL#CeyI{< z(Gu8^jECwrDXR=dBr9Y!^QBi~hu4D{4Puy(jWk3CH1&K8&Ef0f#1)k9*m-e)U*==Q zyP98~{9FnYL95hGWA@Wbz_LmNx*F%yj96-v@xvLm3#9WNr7C4$w>Ci`QH0Rgh$I(u zT`jBI4XP-gPp>FkF3QWIGJ5)gEUX0Sj9i0>V5P4!eN_cK-pa;}Bc%bnK*}9-iH1W} z88SMQ((@)JRV$MNs7)kEYOEoN_PlDgTqZ?NI$8^mC=+%{xXihg+voMG6txK9xRzEa#BYOLVz_>6f)m7uN+MB;L*+Q07nLCs%8Mjet-0y8|# z@8GioYGU{rDJwDKyx>Jbl%<_1RmnDBnh?!l*TiaI71CytHklmNY4$4N8N2v9tWEUA z9Uet!dtI|gL)EYFW@Q4Ds9j;4i1iJmd&gr8NdPyixl%EH)drpFrNzV8jI`I_Aj@X)GaijUb zt=A2LN>D%^hn8KxTi?v9ccC4A8Y?e;M1jhY!ahqIVjTz>*Z>=<&)1Dsfc*I628r} zQV}@Wp$PA{2n9t(1*npsqRuD4UykAYsv?NKgs@E-%qDP`j3F1&1`Nd? z&~TEhab&1|2#j;rmXS8^aCQ#qxpEFb3jf;Qg^lLyQc?RgS5D3?LgQ$cEZGhtKg&f%Vr4(SzlXH(!!&6L3N+xriq>? zY=JsQ+Z#V}y{$Lie-4BoNPE+vUr}Ke3C)G^X4prtiDJqX?#-HuP{@xB=nv_|nLNV- zcIoHm7z}|j;8!Nfz3YE=nDn(CdsD(Ne{jAJ;@L#_+c!!wHN?CuDAQsTE=w72a9#U) zGI=jjMQX@PteicEg~O9m;uon(O!?zga1(|=7MMq70XEEY#7{EzIx#l;E&Y?t4_(iY|PjbRD;pc%wl^^=`p&QK?QpS3<+{li8|bj_Vdm{H)CD~2);j( zQwx5B>@rSiP*1KUFo*e}7?1wC%QSjhpSi?fcuDoBKTJL-xkf75g&gLC04bAfC894I z==oAZ?J275@T;clgKduO3oV%2HVOuogkon6tM|icDVfa;o3UE;o3Qa&?OWQUX>#Vt zmb*Sj*Uz{q!aH>?*!BGOl?NHckG(3iFF5A`flAFL#6j69jz1Tj2h?^8r4@W|R){l& zf0nv}A3TT?qNwY9Fl3$TDS-)fdWB$00h$c*^htu`Z?ZNnhS!m^v7aWoIFkD~k&*pu zM#k{k-mIp{ePM@A<)h1%#dP%U7$Xkeq$Z!dfs}e{1A>1!&TYTuo37Guw2gUo?vgSS z%dN9PmLEx~Jh@+%m|=(326rmvYeJc{@m|`XN!EpbAKV-6&z*Pd^PNd|d}1+fdjpyX zyOp6Q+a zt)ncpatBSxcVe$Sz9y%VccqsLuKff-KlOqf5~Xm!m$astDS6v`NhVOU{e|%yF-N2x zpjsfKL%){h;oh^eti-gsA<90D@Du)do$^-W^=Ywt%=Q%Abuh-2N3>0#`oUE`Ti?ly#-zT!81{*Kc0zF{^leS zLI8;$-4t|hK$+e}%-$V$G_4(H*f4I=-(SJLc%Ih%OlGT7cBFPA_37~ET_XwdVTm8w zRFE;zfeBBYNGUa}kLGQ07r35H$sX>=M$jRA_r_@eoI)@3RD6xkoP^{nAT8AEy*mg`iPEj)%TL4zphc^&lcI!#NgN_YL&^7*&|WM~>_atw%WC?= z5Lg(5D4H()G?H3OGWuIVK3bhz8Q(6I7BOQ7Cq*nYD03O*Cm#Q@n>egDJ=?6CAf9fWiN#qB^UnpU(i6}Oy~f{fT1 zqu7qW8mPx;nqMH_p#7bN$Avd~Tla~Xov!LepqNgpzX$RA^6p?#VtQG`X$e9Jw6^+% zF>*Sx`1P!bJmRig5vq2?OK(rBn7P34u6Ea^(s4?;Y0R_h4$?i()0O>cDt;Cl)NKOR zM$(;D!$at5EDcazS$)|!@jQ*lY+>{`1$<@-HXNbKKK;JyTza{~@G_R8G41gv>307BZH(Z1P#>)DMg-nepCTPadzopK$($aS#{~lF0iUT znZ^X|H2H=!qh3Yb?(LEQ4S6nEmpGJK z%pME{)51aa7yU0SYs-Ps+ibOjKO**DfXb-hB6VVX0YV8vl`nh7w4NU(pIH%9jp^b= ze9*(|pLjpa&BevZ*Fx7Cv!?FXTF?%1Wf>q_zjRmabV8lj@qbfH|MJMyiI|Sie&CfA z=UbQBp`IY)sSjckXGihI0k9r2nkGla*DL02P%zs_<5ZMwHBN6dhAeiWAUV|oSa@Vd|QONkS0)I z#8kp=E~JXWU~7#TT^^9tvCqA9RAqzeQ&6svbF__f%I?a|hGaEG^&^YQb zYL%K!lq1w=Qt>){{DbLE%Cv$^8)z92IO}uiK62R)kx@V8mLA|YW^%8zc5=Qc4+hhbqVp8=8pEWuXgP@%Z%S5EXZG(baNLk z682L_kHk=QBLUd>30R$BBS1pgB9Rb6aA@-EnGDjbem@arQ9I(JG#~3nc!Sd0(rkIfxq(9C8xH$1aX|!_)EV z;H_o6%SM)u=7GOX^?rfqc2_;!a}t5q%SGpM_8*m_0n!2G^^}&gO1Pj@-99ySH0=fY zqVv@o3nJCcadh5{0!d^dRe-%;E|kKqA!kY%IX7NB1S?iy%&|-G_2Svl*-nbf(N3** z+%66_^J{j7oc-2v#=;fXDFw-dC)hq`#j2k{QHR3N>E1Xf>gts?2pZKDG4{}6YsTo< zOa4LJ*}5$VV|pVW^vXe0z>26zAJ&OV^0iYs+;7uNY;vT~$LjI;94RXZpE2OgRZnvF-}tHu9*{^O`c7a;hEVCli~f3nbOOohXY73;#6l-Y*s9P! z9Jscg6$ReNzkC${N}W|x*8MJjxC|v5iWVMk&ay_|ch#pZvlxXu(rnzC)Sncn8T!7- zk7pN|swr|8Pcl*BXtmvTfAoXrHokW4RMlK0gQX&|1V*CFX1zDk4kosfv^wenx`o`$ z&epiEJ9}E3?yr-mT}*NvBr4aa(CS7^tb{-kH_cnrPu)=_AV*P?@E{KT+?|~YFNs6z zavPN1X>T=+FGTN7Oyi4TB?-dJ;Ae-dDf*1nxnW3A6XJPo6v7r3KT<4^6LW@1p7}05 zS+DsS6||<(CWP{gZpg8j9lJsmr6GYm+h|eqg42{{m2fKXS$A-YCJP;tDS=)hd_fNQ zjXcwet0bV0t-ahAZr5B)lyodW?))h3hp2+da?1_BOx{yZ2(XL~W z`>^>kyH&z)po~`g5v%;OEdZ?2$hYq!P1#t(s181dwtT~c%W&Ucl86Mq{dK&vldlE}u;HKx{s;aa3jKS`n{|yq8%87&^ z<+MbX4pzBFv2E)+QLn%ph5&F-5moKc8`Rvg%uRzdkljzEB<>Z50ot#IMZFM1`&T$> zB4t@9?d70vkS{Iep^C^U7R7$0^_McLcK2rUK9{m9lU?=2em{UN=)bxy{_+JqCqC3x zE^n2JT52*{xt40R8w{VPX_lJpPqEljO%;g-%6hfB$cq^JXsYu4*+6G4n})NKn}<4Xab_5H_Z)Wc*+?W6qjjh+)QVIIKA&*2+*`Q=>+925dj9yV4Q(Q(Mt~6;)JrLy|Zf$ zBSPE1e)O|zvwD46Y;ezYv;8C;?^=^w6-6>W0o}QfnMFHd{*3OCxPqDTgs)wYtZ zZ~Z_hVAps3vW4w5u+cWqdX8_E2dY{HsP%>JU6w6QQC9yRj*BMQ_I65zUj@S0 z+I;v$Bg9E3d$y{^0-A*}nZ>MN(@IN%Y7H4^(_fzoyqz9mZYE+r_J(cl{rK(UF?L*S z+sK}LIh2Z+<|aCKL=-u7b8O`&lZdC@Nig0zpMGW)L}4l&}Cm z&SganhP$Co%A{LnW(iPgISB{t6oxEe38Sj8%IW7*r8{^5 zIdw0hLwFdVigA(x4QNJEvar8Ja?0@;S8GI~G^#`@i+CH?(yYDREk~dhb`KW?F42)D z!;(hsg$#4%^!h|=(>Ca{9!9YiqL~i4> zIrb>~@=2hyzsr_Jt;GO%59NLnc9v#0-8AM{xi#D5=<_!mvek*iz`Ti1k%&iF)uUEh z3F1J`CV;eFZ#qxpoTU3=jst4Dp4B^rr<=@m{V^nLNJA6vBxVSAG`nHll6VVow}YLL zjr?+o3;+OG|I-eZ{oZ!-@hsoJ(o!F8TVLtYK1fSdzQ_Mc=J0g_3l_>!2tvz z(80nron`ZSBLDy__LefZmdz!mSo z5fhs!f#i?&Q1)jJcgGs=%PSW#Fu@tNO-_dM7y!Vy@Si=z9jxPm{>U}|ohj~M>}c!k zU})_4XqvZ~X`KFD>iNUP!*I`2ymM##^@RX-V=$VUJDHo>*g6XV|cOP@`{KUbaW)CRFAmQa}fj*^jUZJa=;m-R;kyoHh`Qp=Gjfd~<#3O+hK; zqG$zuYS8m$jWB)7HPXfPv|IkUg@%9$ouZ~zimOJoHltFJg_KBg5iQ+!NX(BW(?*Ck z=Yq8TteQtwndW5QeOLS^gOO`1rQv*P5KBOb&^3Y6dkW_%#wrZ@Vdp0)p_v}2z2s>N z-mcUoNx9HfPOhe1#?=jmMfhJ}pQYchWdFK(op3WhU$|T`OO)w=r)X*Gx6;QS_Ikph zju}lRC`&}fIz+8s-=@^{?!-EzWp9Ya4cBxSms4Yd(S(NdV~J3m#V7RkUE)K8A=@&oq*1ko-6 z%Fha9GWCP?1>pwD8C_y!)uYgH(wD*Cs^OKCo2_3+%QL+&str@ixyFLjJBFyy0703{ z9>r2(BbVYcyOp!1rz4kFt6>ID-&x(Xm`HlD_Vl!a9Wu&A=0L z14KmWQg@=pRL9m4v@Ml>p+eLwMi75OFZ=>x$IJdbgzPdMf*oNG)wfIt`rx0;2{1c(=6qES{*x?J_*V{4qMT>)`4w?qGS9SRQF{;JM5 z7ameWcCA@bdPr8d65)L zkQ?6prOLmP_I$a;-eW~h7wwY#YrNiUV*WR^}I`E?Y zlbnT`RBnn{6wC8PGBN1tboW<86`xS3^U}T!12Ku*;)$CnS|nwmZ$XHhZFX?pLm%O= z;-KU5ZuCQM&ztPyTeAv2^uxs z`IcNh@NLfHZD&-a$a9#b(#e+B+O!1Cs2@HhciOsAJ5X1a&g_-v#gKRryh+%IXv&2l zZ^1Vthq2|e)GFY9 z)N9=2g=OT&0b;U8+p(tZ)!Uz3Tk)QWMqbd;Q`Qkv@$w@MWDhULaOYDq@u`?x{JhEh zp{vt8j8c4n7cTUD2RkZ*V5rp|r16d&pChhELhvhE@u#AXt_I^%`64j0%J#TrYfl$f zd;OqxGqW1hU~!yaHGjClXVXi*GUvb@tMzjxg~W~FNlQLG3;k#lt|&SQ%lyn-fR}t|NQ&Hk8s0IL+6oYHa`SP)3?6rkP%R{Q-z z0RSA0ot(^VOdbDS$H4sq04mC0Ip2eo?5{!J{qdhqo8azIZv~*hO3movXlQF>{O_h8 znD$4tp?@ufby=<=PL7%s2bc^tCIQ%g(th_}?I(E;_qVVXj&q(__>J(7QcFCs>2-my9C&YcgTM zT~_{Gm7Zc)wA1KTsavD(~a& zSDWGi|LdRiwf=V@|At~bfC82h58QxQk1oc2wZ3cq&y$4o0ZD!-G1C+H^#4Voct9%s zO`>{~^xvlJ#RC%B@|{d|@rp%8vz4IRZB??^PkoVtB zZ-oEs^=}{1qeHp#4gK97JU@4ip!;4N@q0t5_LEoo7nI^r-km$>KJU)=bKhs9_<-l6 z`7hodo*+qP|El1aX?ZQHi3i8Hb7Ol(cEzyH0vb$7R_yH8iwQ|HC= zdGS=8o{ok(41ofa<-j2@KtMoXK+t}cskSdX`6obufDogAfWZCt%4lWdZe!)>;y@ZAs_J>((Y)Rz1T5;c^TJ0huHkre!>pq)tWa&^qu9B%Pc*x%a>EfJ;o z2k$IedS-Td_HD+wp;=B6t|BmKdbNbTY{g2aO}AE++zt@?nKea6zlYjUBXVv(fuILh zNm9%)l@^6X5SHMkX@|pV65!@}(_)A@jbdw9bk7B@F*A?#@2?kT@r8jp(HX=pG@lJ* z7}nCC!P%Th`v$WrJm5v`SJ&?x!H#glP5>wmnt|riKe3js&4COHlw`n*F)^)FVLwj7 z;^3g{qXUH9=usfEF6ybZl%P@ya&TyQGWEuWb>Qyykj}9ab09O7t_Az3HVX0~e=c(m zRAT;=aiztwx54C)hx<2~iLV&+=_bQZH_6FyGI{`#!~4us1uY*QS;kZU>6Bb-Kg3Yy+JsAnNQv8cUc;kFrsNlx6$W zK^+()rK=c|Xy9z7gi)^BZxS>KOLZy=qg{c2!gV~vv=#~-z|I|Tz!u3`>MjuRD#eg3@bOoB{kg?PZF*YcLCkFL zS@#AQBYAofT-8<>!6f@-e#tjUzVjZ)kaH)+XYjj%pNRg9X1;x|G3mxlip;HdV)-G) z%J`QS%Y%dB)Z=OrROYXW457s%?93>*3e5x@D@U8Lt90MMRKM@3tIOJg+T zdp!1nJs|WTQ*VQGt~Sc&F7_0BJzrBMH!yAj#=f&G#HfAEDyBhG9u`A6ianTutf2A% zcgEUTV^+CsdgiVCR}ET}ZI~#~m;p$)*P;4plj==%hq}hKbU&jn#oL&$h5b|2FWm1eTPM8_b)WVW%cUkM^cY2y|512K{2K866${7h zMHsz@wGv#&RaUf?vt)Ys_E=I9Ot`khxwqx`Vv7tvsHD~INOhJLGQ_b%e7LN9Q<){q z&`4GiJ@IV9+%2`Gw9e&CVB!|U)EdR?FGtKw`7<ag`-Y~u(ewpr@P{{9XamR`12qkGGh=dCMB$5aNlb8IxhdbWxo!^ zZUhrc&F#b-+T4?xGng#*A&fv!limX=-&#U!gmkja1mKM;6d4{l}@)>Uc z!=EF_QQWH@O{SUWE(ivfIv=zP(w?BP0t%6Y2!0W>)A37K?UZElW$b@{ifqar7FFcU zB-XO9MK%nGFJ%4HmetzP%hCgU#RIm{v{?p(dEu4zx~4{k0dA07?| z56)_e0L`d$We<<%I>4wBq3)qDdp6zL_8KNl7ncJV2cMvTpsYLjwoM2k!{4pjaEWQ0 z{@bwO;$-JxtrW$aRZZq*#k{9HtFC7i+vw-Cs&LUdh_~Ls9qevz*LTLsqAYXpeb|U3 zG7>C8_&m2K=g&>suTowos(^skuX%GW?Bt`{+bb8h z-T2~%`QO#+AAgm!gxcgTmkPcvN)vgxM`FR*LaOuAovy8EvwJQb!?a(e^~P9mJbPin z`yTQ8kn`&g54hG~w_t1ko^=1XxWrX{?*a;e`Ux0?FA)6rH)-XNXK`!M(_$8R5z!VZ=+UdmLV0ZE&dO2yu zY!Rl;Tm{i>E9un$VwxIPiB^&G*tZh#{Wr+}gxLSY6$pq`L#gWjy8Pb<%zu^9)6LAu z+=A)0T?Ej;2vv7B}akO${v~_cGOwL8&1OA_Qq;qi78Ra0k zM+5}{!2$yTLHMt;sfUfdIir=UiMhQ+uCjpAFf&SSWXiX(M(P3*#RXm@C?;0O2*^bg z(y4QOs~`2|gz8iO;UDDCKu|}XzuUcDH#+uC<5C_p@Tt+f8dt^vRX^e^RIm;|SmmZQ zodQ20ai)R*E&-e^Ogf_IxAYBt&Yi4n>0W z3pZ5yk$@h}s29L~QpugsC6+CW{B$_w{P4sJcQ$iWj_UO5c3Z_g+r;C8m`@8v+aLYcmDq>n{8MAc)P%LPAA(9-)YXx z%0?$$ljhP6A02>;ii@jLNrH#~1_=TJ`X77}m|Gzvl7R1@!e46U5*iRTl*c1yfuLWh z^KF6gT6@G%-}yoJ0P7^&q)b#KUG<-dD1iw@r3vZ91qNV1c2RXkagKwQ+U1pXR-n z_l4lsYBl2 zOBHV@_e!!k4AQ6S(@FAJMiGCr+Vwk}c2t<_D{%GC)JlwW4$9)4y7^v#ehF6E^M9YbacTxr@fMw0oJ?s)eN0*FSP`+`0@77iEI`aZ*aBTiKE-Eam@kPa_< z&y4|H1fn-$!9K4XALqU3+T3VP=&rYarT}uef$Q}PO~Mf0QHLAI>>>jVixHiWC}Rt| zBRt-&s@`#CK7G{26e^9c!6gjl&t4Aka&;uV*fRu| zw<4e*(qEn8AK!$K-+`d53>M}dF-H2Qu|8SOl2s7{P^2;-=I>aoKCL)WKb3WbH-Qs{ zF4!SkzIOQLqMy~>Swwm50d2(NS_>X!X{GG#s^2BWR@aO&GR40AcSMtwI< zV?q))Nv0s<@;+h(31>s+69^?^+Fr>oL9_@XVYa5sHdMUDJM!gs6=vJ>FxFJO;8k(k zucEGSt)xg@!F$rO^5Rz^W89Y|g1}!?A3=(sKVeaSlT{=joZZ8civ0s13PT6WjLVXL z5c0&#DB{{=1X?goUb{};NTV+?3(Z%3FzZ$E{Z^oj7I&~>C=$U4qfcHbu3)Ps-(fku zuB?lNrE4)bchA-Z|G+k=t&I)ln#)mBOm-Gn#LC63`=JM2PcZbx-pV|Hss*ICa5Yxi z|4=w@*01%nHtR|Bq5*y0SLPXlLIjD8v&1EEkMjx3TXIq_@^5 zuI%MpyD6(UQ^F@He2a5b-vRGv+l|-+{hT#C*0~C^D^_{Z&B@LZJ`PM>GPXx0y~d}1 z@G=H=R2W4zgoQu8`!a$Bugfpz8=iSQ1OSn=Kr^n(3na5|-N1+lgfmM;TqcvT+Ov*;5N z&fdk6aie(U00@y?FmO!>0kI?SN!u%l*u0L6Jkn21lvQ44>U~oE(EiJQIY6MZZ5nlv zEKzaUpv5|cdTK@o*U!6Cz1fI6cFh1)tsd#sOrDP+yMcP)%vyyr%VV@Ot0XDC0PQ}3 zw{u#d#{I{srkh{=;}NAuk;1j<48D*JubV7ql7_?XnvSO>Dli!MG`?bWND$nTtJ*GD zaXZuP-J=WP;pAi2B(qrH0^>6Ux!AeV<@l#{tII@e>GFwr)gUK_{)NUz!YJ?9uy@%& z)Na%_e@!<}+3p{Y&Z)se?3HVmuv5$G-!(Qj$!6X$(+EgSNI#(pYh%)DjW3V7R7nnMXjQaB z-^(M@h~_+Us@DFT-;d=2mjFl+FI5(n^1g5%yQ8?f06?OLI_tIbW=r;s>7QGz^JRIz zbJJe_YTd8n2U9Db!CdB!(;$AKIO_v#oO#|-CQm7dg>j<7PytizktBdsh5fDJ zKYOox&HyS`XPo==_5y$6X?9eq*uzk~n-cL+m;-b?U&AV#>GCPwp^*2k*6AAlu+XM) zcop|~#_n2|gv+iUm^M>K-=TLdWkuS41P&Yl4;>~3z2+U5MM8ayu)1iY%qB!`obgEt zzoM0~a^!0%$n7{z06$4Ri5Ut3I@M+`!9Df>6%g)qJxSXCZRx~-0sMmhF2wq#9r^Ao z!))|wZ*Jsbe>37-;nKyjO#me|p9^XYos1Pk@jlEqGjJ^U zTM-Qf=^E!XpDU(=UWVNXuqoCN!b>{xZp8}<8#94VXDyb8+Kyf(YrW#&$S_j*3% zSd9HmyZy~NpI6`NBB=kTz@Bf00o4f08Fn^eY-Fy_7TF#-eEyLK59F!$&pmO4(|pn^jP6{Z96+mU+43rByB=e>zm&_}T-@30)@ zPjFNDg!{0ot@NF_5?G8jpI5kt1NBx1dzVr#;v+`owr+yRvaXYRJ{0^->V(QP_#=va zm`_e{kEMZQ94>Sp(pf;`z+VJ%7{FU;VR+_<)8IE4qyGhyOkee@>MY+9(g~`OLtpC= zc<{O|6m|#A#kCxYqe`<6br+sV2QMbd?Bz^GYtXK9Bie*{wPxWL3B6kM8l##7IsC+8 zm(;vI*ZiwtT1e&D+JrrhKou-V2F~k`Y0>%4q+cSv&*;J0bk{P$y0`2jMSyXrW1EUQ zy}T=$#|zPtBOXn!`mrz*^;#8HEcUKsp2A7ABeU;K^wrBYPd>3ltdzD4=&fW88LB15$e%J^XUAqXSi={Cb2ZSQ z{>iU=1f#8bSHWePM*!^5j0OHOw15jrA1HWhzK)mVefkjAPjJ>chQf8n0LeZV4Px$)ef|DS{XECNVB zlYz)t(Rb3MnK2Wu(2zlFI#CPtZd188p@h(Bu6$)}FW;z-0kVfwN@S9BG$GcaT~ckO zrAFw>*#4v={{!PMD3UKsy2f!rQ}LgrWOcFUjPYso=c}fMS+q2hTeuUYk*Ol~NX>vI z1V6ShW_b-bK7j5;%uZ47gUuga6vfEdS-p}?rnJA_a*K)W?+LFtR>MH>bMhoK%%kV^ zU|j3<+~aL5RZQvQOM{KUebQhN1RUfs7!;)8^Lt+YyMhc^Nkj`9UX2f1&P)vZ<6Bvh z_M3*BLL$Y;0QHe$9tQH5+EmjRGqgYY$7D>hXbGI$Do{q+u^HT@TRHW0e4-V=vyKf} z`nYp?t67S@S&>0@z+$q~++%g!yvw6t<>M^mZ$tux@>oRla1`44=skBa0>G>ch#SXH zyj!3K?vQLMy*r#=4{a7B430W){MN((jg54S^$0557oZ~rO$`h&R*$8ZV~aOdetJ#_ z@{XQR0mS%*>1lKMOtq!h4_2`h2uAaRo1f53)UFFgyLfG9sQ&2W+c{D-@!2xSvK?A@ zAsozTo8yN5;AvJT%|OL6RmYM4O`Vn4rW0o|f&-o|D5s{SB)>x?9A#`yD^--bBX=cT z;zwtPYryY^zC_&0GetXdZGa+AIL+@Fxqc2!4p5f2R4}iZXq2MAs9nN8 zG#YqYvIw0(dW?#STY#QBLqmz1GDqrJ`(g>OtizJmd1Ojk$;TRA;j zI}^sRw(mky^SaK!W2cIxlopp*dd^rkgJk^FB-w*3WUGFM($MLjV1$iz@qoES_7lO_ zA=zNh78~{)^Bs>W4}cwS(%rHRmy*Y+YW{u1|yh*oA7qDK>s%6-o3?MAJh zO4O*y_ZQ7vS0jL&g0G`(zs89@pJ!($xf{-55Q7+P4<9bGj%X;{LQ|hR`|xXY0phJh zQ(QsXZ9B%ksS9~0O154hUTtsg6@L8!9-Lx5!#f_H__MjM_W1$Z3YB`)GLZHgU}<;EnsN8>^ZVVkp=W!PvCc+m~_L$-@BNtuN}M9SEUU0$Sj zVG)qf#38dT9z7%eD|jhdLbD3{076Y{6XS08a(L!m=x&k7WdD7)z9fRkzY-3DgINFf z=YsO}(t87Vw&F2JBVV#;^>Zgc*+46aP%JzctLbCBzKO4nsAq?WqD49;zM9LN|xN-z?4SMNnAR?R;w^k3;S2GfbMVD7#K_LIQuIaAlIU|^rx5C4{nezPC`nxWl{ zJ%xR4A)hyJ9fE`VHt^Ryz+8FWVT|B|c-~1xU-8gm)IW{6m#}<$?7prao~Txz0<)$0 z54VEnLRZ**M?4z-kg~VEv!gmuICN!qoZ&JGGX80(>V!1i-gy$|DO`>SV(OI7xe&C= ztI-6aL%P@->Y6w4rC928?$&NPp((adPYYH>;#ut4B@!tUY;tfMz(m76RJrmK98u|> z(`<`<&@z1&SLlR*8Pz}4%T#{5doPnoqRu@N;$GD?f!X>lOr;*tHPX}|Dzl0;+I$wV zCU2MLdDTnQ%(aSc3FHLCkpSKGQ}f=oToceq#JaND_w(P>Ytx9_X6bLgXPM?#mEu849giU4z0w)`OPSzBMg zC-!&pSc$8ZszX@+_U7hox#&Ku1VnmVM@&UU&0k;iTds0vppu`AStc5R_$+37W+pcl zvSrU$qNA^OY;<3Hu(vM+?%3462$^WGP4VSz_Re37gTomw+fdm)LTs7AV%xl_OIq5< zn6)PYhST09GhwNMeT^|{IBn1uDYS6RTp-CL_V%-}fSeD#80oPPQdabrz;ygpnY&KO=fuh|O#dgrF zDS4gEX)PPoXhJ43td^#m`v>oQTpyGHRj4w);EgHf&lN|W9vB8u7ADZ_f4m-h)cp}l zRZqN*7-gc)fjILha+P#Msc6A0^RdUzJxQ|Ea7Q`NKrZ404DDoBBK^2a_17PGC|wy> z>*UQSF*YbNB}OXVUztH}*p5dg>&5ZRU0sHne6yGjBJR7;^dHSbrqx&ds#YEGy3?Zfn z&4fy}(e{82U61JN&xg^F(D2+$M9k-@aDK33!L{`a3P%0tz&RKg=`Y!Z*PDx)@UaWi zGiuawtm&9koF86`uauh{H($2^<)%zB%F(+Ma9ukusHhP7d2avp@eRd1BK{rVV`Xt zKm?hb<1fiWW17&c02p0}1mzm-E&ekwr9rUQP-6r`lO~)VSMyK?lBj3=TprL!wQL;! zee^UZ_d1aA3Pi3Vh!GamG@tI*L_@0*RJ{8UC`+6EgjF5G)&$#~0`93RolhEOr8jV} zugT7`boInXw}Xlg@?n8y&j-XLt(K5{fRg!zoV2!oiCakL1a)NxhDWYE#$wu0@FTFR zPz+uEj^+(IYKy&MW}|zXj-J?oD!6gf@6{SrsWNyn@x}L%rg(o7_oOlk#=6*2qM;T> zwx%^!t)P@FCp`T@LLOad83X`*Yo}(v-SfEJ%TA3gPIAF2%3AP!qH%rx^!0oN+{^e1 z&hB5I(_?!oT#C~*)XMhUa>i`BzY@*s`KNMkhmBmYkxoXZI4!9(g}#VnvlTy6^!-$R z03b*8LRh~rw&UMUdnhtZrk8$^___mN-zC;)-yS7s%v%DCpN8UR&Hu!&$5b%)oxp~T!_*73yG?Ip!b zdseBb_-9a|YgeYafYRqnG7seY<0%*d#Z+0{Bjus|q8QLrCaj@Wj80#wKeG-5KP3B0 z@P>tA`)}*cpo7EHAgPT-*_5CwJlLOAC|GJc8Sz-j$5F)qp)^AN@?8mFRZ|*r3({V) zjWGMKTm3r{Zn75v*AX$qC)d>W1$?McyO2VAGeJ}=3{R`vlv28I%oJu|KIHPs0{QGMdA6O;!&~>0~T#PYTPLgf%#cV z+xxUH+MpA?VLEhOOu_&-*myGxKMKa3GJ+uiIqES&?@3$!?ARS#jtN%Mb=e3R$ec3E z%i4phH0;&wYt{XFu#Nro*IQ{tjVlZwL#DykV0P>G+FbFMh$ujRs1@!`eAEX~s{1oe zI9GPFor)s#{^#L+*h30)pE|&W&R1WG7cq2|68yT|soZrWC59Nt)<@|TbVUge^k6$r zlmrYlZSLPsYkYUuSRSFMY4pw7DZk@C&3U3y=^vflQ$5Vq<~S~>>WOhxjg zR*Hm9FIyQ^BgnpQjvX+2^I58zFS-bk`-hj-xfS?Ay~I*?=NTujcFWcjVh;_Um^RQ_ zX!YlG1_v0Fj6wkg(S>S=93mQl$-5jhCB8|=S{kP$lOoM_EgcXcnnKM5fy;@hjy4)G zox#~ixXOQJzQkOF@xd~os7_v3)Aok5)bGef1VJB9Qe8FQh=xK&@{%_e$_1LJFT4IaNn! zW;c8zUk;Pr=?lRba$)@>fg|n@_D)p-+{i|ZgO8vO_aT4E!QIao%M&8Ofag+5mA-p7 zeVOTDcZlVkUWvO#bnWZ#Epacw1uwv%$+o>nXbw-&^%oT%Bm3Ag*{_HxLBAx)jZ%B% zH~7n+Y*vL3p*_0)&F{;Fy9()-_GmtYCMhV2YB@W+fk%bMt<&TjmLjUsut3 zoFMd%WP$Vq6NdZjaG=!#-nD-v&kPM8tY3M~AiHWr*lYFm5Nd_Zx5*3SfIZ#@8=i!Oi!LH@14cHF)5s z2~7#~`aKiQ@EV8#tBP8Dnl~_J&-4)SE-mR(#aw!_iB`6T88f37&-Q~zfZqS8 zQhTkul3;7lbA};ar-7hJ=zXG|rpaBJo@yjoDQYE?I$lLLVNyT6X#?9PW5%WA8ylJ= z1QBXVm@~#eJ6F!lt9535O_6TdRI|5E<_+}EZZx8{AI|Q;y`C28&&|uPtyrF9qC=u~9Nvp22SVv(Y)pL`kHrX&O$HM#H5QpF2Uh zTw61p&o{-$-TM)#NyzJ41yD6F@q21FBF{F@GH&yv>!;LlI{N&K+^CAv-s+eDMgU2{ zgR)1~V~aG)(ALt@yYcuU+tSp&qtv7Yrhx6(rkZi4RJ;Z3=D!*mHZ*VNSG06hYA2-~ zW#Sszo`A(PWti%$U&bj`E=86>`eu)s#>i29h36DVQ}dUBA%#F=%RM`PYl_TWQ@TFu z;;ysCJxA-7{^P{mq^@gckY}Zq1Q0^kywxSu=+~X9on)7>xUJ!9s4u9&DB#pur-KVy zhyqvtu|@H87Q4k@&$fMuyYKtmuuMKZh4M3f3jv_qy9+LO9Qd=IsC`P#a%iOy(s1-O z97H>WHntbrH0=;Mcrh2JH`YKddkJ=Q5x;Z*ZgD=^H;&^#>Uqs`g-&Lf3GnzJO+m)Q zKE`ZjuF%Jxz$q)%cG z!!7qv3$+^)Il=t=Lb4$ld&|*d)$+!=mA!wTF18h>9w#o6dVh!cuz8ELX_~znx`a=U zNGj;sCuij%{hEF(WEuCn10FAHz(RZLDh0YcklxSFRZufM;{4b|O2@*pd)9VN;T@{tEC{Q!QH;W1hCV6{oL`JHFyMlcRnxQ~vx&K%sDN#Tu z%|=}7iZQ*seSmg{3Y$sBS#>MrcU2^)1Mu^R@rLQz-K}6&T;0Zp0LM$D>%ULV44MGK zahYAN#nT(1}|;`(rbXn@G?OmZG=!W6Wv~j$gi|8 zAHVX5`0V7NPMX&z{OAC0zxD9yAE-7Yzp$8Sz45^ABg5?s@6Z$scW<3#C`@W%avM?42gbpygU!-Er zyACAYwgE0>hQiDsOSu=#ih>IB8$4L3&^Udake~Zc&?{U?t6aiqeHov3-qN0gz2dv1 z1k$h27v&ec0OVX71Z{JW;4&K2S#vd+bEXcz69W=O#F0?#`&K49ik5gWAu-(1efdJKDMFge*U^Qy)t zz6=&USMhM_eZC%qc|M*i7R@80KV$k{QMF9#Pu|oFfUGAwLh~Z%>6~!Q*qa$!^=`pc z;c*<2jx*=ZwO!nM8qtatw(o+BIdvtBiYWJkjoUr^2A48EyEKb|eaS~}h3w6ph(lFq zQ)dT#QGtoVymGsUTG^+(ir;xgM3j0Wn=jYp7dbrsuRs33w!lo+{F{C+s+`b!@WK0E zrvJHQDM?4jFRt-nuWJ7jT#e+p!=6P!h}d| z$@w)OcQ6$nZNBapNE42K|ufq8TibfqQ5Y)KfJI% z2Q29^-K9^k+tA=(LEZWLt4{co^ue!!VdDrSVB~ zI6BeKFXy$dTD-A0ay%}1+fnJ48Xb=1NgblvR(?W)$Afv}oZx5Dp~e3>jj{9UpHc5j@1f#d}8V&_S_)qaxCq}k9T^SEIA zvw-Gt$?3c=Y&qV%RzAZ`?q$OUqAj>4NXaen^(PYbQDt0u))b{ykHXQ}kZJ#+$qSLZ z1<9JFuxn+3(`HOHW$!~LEJ3Sl|4a2<30Mt}41%&ghz|$1ws3Q4{LMp?uI)$l{$qwA zeUa+e&~3KdsQ_a*!keh5b|$5UUrXupB6!x^k_qWS=LN)UtXj#nMIR%bxmZg5hu zV#BM{jFgr4_ajAk8!xsc*?F-NlvIR+?nw1e=e%QWK2rz+;XFa=X>&PT>-N6NH&&s* zE`_e=Ufbp#r~1{!JT8-7-BKl}lwnS_H}$A~7UDS|ArU+LgeJ zkXS^ELD;=zZYgo5F0UY30{{oFftFNHlhM(5zp#nt*s9!XcFZSd)u4An^@cSaTsiee z3|c+gw9{K6nG5#fEv~Goa(FS+hOZqB-}4Rpan*uoU#zW<7L*_W^RN|dzmV`X9lsLD zX(4&ejiCu>1}e5~i+4uM-LMaX%NkTOKln`1ef=D{gnX=r@se?ywk7r&DuXOC7nxjv z<2}wx@i%w9sH;phs*ZKAVic4xhZ1-Km5r;$yO9i~C7%!<&F`Ht#fQX=qv7h|0|a!i z7v}^V_2IckvHo`88QjWaJFec9AU_?k2ih(3c;?6(hz0K2Ia_1ocss@TmsA&135Ytt*FA2mppI&J(kU$9U!(8mKE))rkYttTD7OkHb& zsDp=nVq+Gj10Vf6Rt8GBp@1+TgaQ2!Jv)3mz-o2gwiymwmBlE9v!$%CDLg&146pRNBq&H@(^PflmpPd0Sj_Zl6}Q`rN=+yDi}rg};fauI8L#)kNDq zSwMmLZA*Gay=^WR$#1nnVk1^li`}?XtLIfDf=8YWb6jT`{-b;FwAIOQPOACo%}W1p zf;D2m2Lu3Ui7dT78o@`pMt9Cj5;UE=yOW!Y5o_qlVOXe!{R!|6nq}{Mw8e=Bw)&Xh z&_dN!iyl0!>Kjsfw|}v!GpM;XgAT`Z*ErgG&dla$-8wgqTQpDZxmt92&&frX!(1u>cVp+{M{OT2sm5VQ@eolutw77B0^hqDS17F3;ZF3I=1}y zS7aTR9umHYbSVmv0D@}ak6bvnTY}MTtItR+pUe|-ImoUX0(^c72dFR~hU|ZwCrCymYXd&(+pghyk;YLV;HeJ&ZO!8xv1;a4vsBW(s z5DtJyz7{i9_Yb>hc--))z~=yssT)8ZVQ?Xj?B~yq(`UV#dk6E-_T&1^H zT$<=F1}$-qcc{}7PCI<2<+yp1Fofrm$=nF3CkLx@3~{pqX80d$#?QXsT}$JsuM?=p z&_DQT5T16Y$9wm6Q?0Evu(s7{sEgbrDpiJ~x^d&WV%aT(wm+0&`6qf`8k#we=5i<1bq*zr@gOae%gm{61Xf~zOwVQZ=sSbP7 z`dj`DLwXHjJY&oe$sRe4`U~I)zFQq+&l3g#DJ1!}c+PzNR@3m{^hj!G=SXdAZ};?g zjlX0&X6R%9g)p>6Yd;G(Hn*X{pp{KJG5IqiVLdMj4oa459%xFW!m9A60P3JINc3MS zO@b@zuHAlLlx$tKu?^U(nOHNhdid97wwmzEsGpc5j6_*^)LI6a)hrh@L5zZertt9y zI@i`O@l9A(T1L4-CE2a0&s=ij_nGm`A7V8#HL2$`3O^m6uBJ1lZk5R9R!vSEW?e1t zo*G3xM%5ki8};Kkf2j1D76Be#y~}6}000#X$< zNnMxte~q2AJojZICI9pni|Obkii2T$iDI3G;46Go_}%PIBL&|HKn%+XY|wY2ob=`$ z^&@ms*>^sO?MJU^7O`@#RmCt)n*XA23Fw|3Yl#RRKB4@3zwe40<=oT>+V=+|CJ`EB z6?`O7lLY`VNijtvIjC!T7qjeW7r~x1zV{9wJJ-lBWU6SN$Lvknn-d@%bjym zvBWkEL`O9Bz+EXJ)WzvB_}o0w3}eCf#ialN`GE%lvJ8ZqJbmkY5bLCTqYv=l?Ge$K zRS50z%vjKiLymJ{{Tc&1iW*EYR#BRmyYA8wx=#(b~P#4)PZ?#VP&VWzSjI9}JaD`08Phxv}bAE^R#iA=cA6+R}z zjXp#&EAGaDyWQ@Yr?NttH{eA5=BM!KPMq++F3beWYJQZby5A-J{B&Tw8fm4Ht?mcK zXQ{+FU7=7wg+{pn)kO+XOy*`URMmld53-ics|%zUqBgRRA~|zR*Iff^G?5kQYZSlud zAH~43wu!lX@cIZ+EuRW%LvA%yM_LGt_!0*NWUx23|y-9*^}+qKu0Tc+11c8 zi<$RJ;v1(*+K*Y{bAahpL)FWK`6uTkM1TR@Llm15^lkVu9ps^JrT!W3|$Qc+A_^SFkJy&)lQp_5|c?KOESiUvNGn%(L49Tg&tZJs)Ssa@-ttCE1?U1~;RA zZx+Vf9F=elAQfSW64_y@R4S~vl1c&<2LjtMlU@IEs?V3IsZdG45r}7>DTd9x-eRA0 zX#_)Z8f=C2U4>oxIGI$b*hjvEKh|IB1~4Q~e#OHs)13As>O!qwqt|Bw4NpY)-d0a> ztMvKR2=1zFDS8iXF@s93!kHEG(Y*GecZ{oUW<7O=I%o;`rU8h zIpk=aCkStkMRdea|DE zn{TNOHBr{sIr3 zGAwS8RqS|Yx#6Zn*S99;dCrN^uFN;6tj8g4W1|W$tcb1ou@O{ch=J;usS^iB(`9cn z(yBc(G~1q^EGI$?h@iWSJXWZ=4>7d!9yZP_ir-AIv$V{1N5x|WDYT`?b11mYa0_@%K*A3gm%@}4Y;+RLJ+Q#SzLNT^_Zr*HF)gx=o{2Wsh$D1qBi*V zs9x5*pIMg;q->ZMn@902BdD#ZcwzVA9h))45NM?Pzq<{!iErDsh}Mm1wAlwbL>VMt@0lP;p#Zf_&o{v~}_l+JhwcOy1;r?_;=$|KZaq~U?G~mglo_? zBbI{*c?Ao`yPYQ2Lanb8zxf%{J?WQc^1@cgFSQI0Ff2PI`(q>`aMcugM=S~}ukDUEp);n|b zFPjW00GXn1#z!A(-wAJAz=S&_I{~z-vuP-z{G)C*G5*8R>kPgAH2L_S8dkTRy%Nh* z@VlR0J9!u@A@z(A5=Wd#h}NkEA#;xZk=GTBAOV{DGtG@Frn;Pm3naS&V}SrqM=~SJ zD5Pgb65^xPqMv`rDTW`5n%~UlWE0QhPo}a*KzcV3s0H=IClV22SG$Jt zE{;6zhi*)uHQU0yH^bOCLUK$>pk0RW;D*hV*6j_g3zSEvnx+TuWgrvViffxTxyU~s z>J;I_f2yGH2+!Q)V*h@ASzrBuf={6tw@fNf`d!9(@D-*n&{6u68PiObO>A1tY*qtY z8K?^z*}hKEj?HYwWr*?Aet_ctX>ziu(rEu+%?p0E@rn66_m!SMXON)Nt`V7t5!Z{O zh~%JNo^-s$85>~{7%O4q9sXwU!1S zTt54u%{WycR#^J^)78K-MYiy!_S$LPx{m!__Ug4%jNBmlO0drT9SlwP6hWzUy%<#} zK*p~Ld#1d`3T^?(u41I(IILYQIJ#D^V4cx*Lg(6aj;s0<&UCaUZ{2A$l&gVN1n7Lv zhVy#V4Q&c&CK6(PPZz<5<({IhEiA6<8YB?sTXK<4t^N%81Jz1sR^%GW@Xi4@3(Gk{ zknSLYk3A+-Yz+^K@L9d!j8?LRqU9e*&6RIbL2kOJVWNm6|KRG?1B{x?v3?Zrrukl@ zlpoh=FMkG(IE{5Q70Z69=db+62U<7qsUR<~CR5Q$T~)V!dF00vd?8!Q3scJfRa^?) zW;q@8?sKJdBH13bHftZscJ%-`b&oFQ%MD)SufoDH%b54wmoKp-s$z9Iij_7H>D=d# zxV(f=`k+*315I^zSZDg|@AAzL$0<+!R@VE*DEi*oPX1gqr<%_Itq_6A0RHTrN2_6s zIMVcD7YA1B(&3;CLf+&~ctmC2mm=fy6NCJ!w8LZrL;Lr;xszH80EzwV4x$$gdWqOL zvLF)8T&S`4Q#=+;W;VbOnxxDokQrHDGX`I!QITM)CD^gb>P}DGA2Kj+2$gbM;q{g_ z(<@c4(T@bJDx@Ke(SVOA1UmA}=oT3)x`xHW<~rnm>KA~DY?)CoKyPqvq+%VGmJ#h4 ztpTYii%`ePSH!Fs=ly9*v+JXO#!i6a>Qig)_kxb%uP^qvc?wWQI+#K|bDMSUMPg-0 zWLQ|14Kh>|s9=m!{b37KP8K7BMF?-&wUvk${Zj8^n~1cUO&Eq4;0v?iy3t6v8uhey z2++WKYUMK9T`Md!pU(cs^!VQSxB2m`<@x+kLxRxYlkmNSk}<~U(#=Q2M-Lw=z=>Cz zfk6%G!pz?hF;TL%NUPh!aG7_e31nq`?X{TWpI%}hp*QJC#uzwxQk_nK8aNeHOts)w z&eEv(O@cpT-4{pzC(Y<0PIWHndo*Ldw!mtClv0rE(5;Qt~Un;%n($k%~x44{V=1@9-jSlA~QkJCa%2@f}37Eqo zp5A|>3;B^|J^cHmN(9}hmImy2hg5{lp|t(6;E}d07aBav;iyPe^v3!f!?K>G)j(AG z`dbT&snk_6^MI6Jk|HO;5d5ZH#i$VJ4e84f>H{(|Zf;nm-1PG2nl74F$!Go!Cr}XX zaX}h%FPOAIpT@atG}oC{tggG4Q!lfxTy1os=%U_F6vZ_Pf~}OD$ttV5>j!j7>!wY# z2{G%k6iT^x4O`V#iOO3e8H)L+p#{yj=kd)DEbb{tv2lKEwKrZuH2B%rcvCc7t`gY) z3phZ>ze=1BBN%kSq7h}OEq6P9td+zKj48+!yj7bxpWX_~27i?-RwJxbEaFkXK`klX z;uch6S;uOVFgFoqh&Y#bD0!|M>;$+%(gD?wTJF^8j=IipH{}BER)o7RJnR>!sX@ztDd$-i;!fxLdNk9WC$H!B3NFj6W!oIdcyj<#aTeRH%nK2% ziHPdjU|y%Vlz-Nu%(G%0o6PiRfu@~kEVan|_57i}$eqs$rupnpaw!A&S@WY!u3<{rJkd8+pe zWT-x|S#wXD==Y+a`h#hxKA`$3_pG4$W1OIS{p_SXg@60n9x4NEjFbo5P`y!ikRX|f zl{Xace0~CACjbJSRhkFzq+b#PaT!6S|{Glp)1_@3L1aS8e4_uPi6JZiUUdSNrC)F^ddSy>A~e{)V9NwvQrguS#4Jn zR#N#^V}FHmEReyStgNOPv?MQeKKp$KcTl;5#r=zxcdli!;_?>CLL2HX!8zB*CrzZ( zmh_|1{V7d>V*8)cG7CEMO(jv2ZiZY63mJMA?krR~sX0%H){41lsd_q;1#DK|4xelW8)PX7qnSvi?xRX*r^6Ud zH1?AFo0&?ceeHf}R~4-|^E%EIcAziG=4V2U&Q}JLOpCFt(ypQ7j8-g_CFg}&lIp5H zv+4>5jHe12O;%Z&wnH{n(3~GEoV%*i+FYq6o>9gbVb{6Fl;TBI+hrSWKcEd!a;Nhj zEPt#C<}X&O-JY|V(1Z=;!Ei!KiRNGjo{p5o3V(ta=_pX9O-YyK_tcU0RC`+G+8kvM zfD2mRZIiiIGtFHsE->6R9@?Q-fgH@~(>MBYd&?OjgAGT@WVNSCZ8N(pZ#ctjx+QY5 z+%zcISi3vNjYIkm2!bizDEHnpo*QYTrhk0GYrWl?qSDRz@Ps~94yr0A_U2GU3KK$? zRnpv&t+Mu9y0QV;>j7h&84dDXKIl|&d%O~V@g}_&XM)$fT20?b*AwI3r71l*L9sOk zMt}ce-b5AxyC%}G%0a5}=@y?}iuMwBQV9L0=ni2_USf^yS3&K-mu+dAKfqLXN}Q3W3UaDU#^NFDTT{^pNZm}5 zx{fGt6sDG}5T>$c##+N?-5+f3f$;jRSa&O}$peU5YkTJmsu^T3fVo}H0DmRR)1@c> z%(R?TqqsOUIXK8gW-hb3(7^$y#h3htJ0k^WJfAMeYgL?o(`MW*+0zPJe!n$UAmJP$ z;+e+eAceZ8f}Y!!P9c4)HIkMnq^b;JFd!S3?@^o=G#LIY)TF|iHl~WKKJ>7mbuK5H zE6Vkor_%_BT}CK8h=?%pnSb%loza!O*6}w?F=!?Djy1Tf<-7p?uP+NWN%t7!9&zyF zQSco%lV-~-BQl>gzM_CMOXGWdZqpJ|5duLyziN^&oWZh!m}wPSi%ppuJz z&k44GVyBc%$X+Z$|DP+gzj0k>2~f2(fSb-`bcY2 zX+vcqj9^GclZnzZ$llv&gf`ZHkK0xK5ZkxUixe&lOVfWTj(;*YNoz~NvdGR&Z{)mj z-fd*;@ca9J0Vxa;13Mm@YRD;2jNc2CQgcN*>9EENNRD9zDZae-QtPo<1sXQ{4DUHl zS!&SZsPmLUgKs--<*uXk&Q&!NAy+$i_E_CC^OWh8sCuYr>!G-u%FLvIy z>fHVvG+&?AQ-3*|gsrmzC98(Vvu6u24@nNu!&+Hr#rmsaQhmKVa^+?#?_vLuTcyKl zbF0>VHqO&szBVg!<+s&JX}CU~d*_gT`9LnWW65ipeisB|Q7L4m9R{b0QyO^ij(Wfe z9=mQHZsvT4p~OvVhj0I6ww^qoN)BB$LL^M+ZQ5=k%72H}^(PU;1_tn)AIm(S6><{? z%jE}zbZ%40*Oke1aow_+S9`p12e_4pkj(BdH9}E`*^tO|P+)Nw<@Qp;T%)s)7#X>p zc1SqXWo;{~huE+UgX}j>F-v6trbiLd>w#!o0-R+^f}4emg4{hHS!#oeB@=K|e%M{} z2m5(Wihos(k?yV@Qm3o~vw9GAgux^!2*Q)?4})tgLVLtGnD8K-sUp@``Im&>2Z~m& zL*#iH9GNg_e`!GjCdoRD*haR{EC`d#AzZ#0((JMf8+HLc???T#GuBCu$`5t(-h7Mv zd?C3D;pUNR!Xd;3cY~Umas13z|jFJ#YFn~yDV3#kY zAAb|0r;+x$86kC>YfxKiYP75t)M|9k18d4jET}d&wY1Z$Y^`g$cF?RMKhy2~`dfRrMd*naM^*#&x?YP50q&?^Zx1>LlRwkIiT$D*Db->B2SVWmP zYbHo>k~q4+JIv$3C?=STZ^&4jhI+K&Q-98Mh(EjECcxcqD$Xc#bc`>(_bJfNca;1i z9pr+ZW~qZ$e4&F_+|{Avg*K_3(oMVQ;TS)N1NDfchgHm_N0v8LqDeS2`QQ>g2X{{u zQ)TMm8oxVbUHp^{P?vJnA?OFb2GXu3b}91dDx`s5Or(du|8(3Tcoa%da~y+}hkswJ zssWc1vuKl8D*0}cTA>Z}C3mq=>DV;71=P*k&s}V69&h!byVwkJ;kjG#U>CDTTZayE z;o~PC&-NICrH@iPGw4f<4QQ7V$7ZQ!wT)MNV`H_Acr16DNQP9Lv*_#?@8(R=?|qD! zVrCj|j05jLG`{0A!O_Y=NwoneH-GV@gjg%n5!%sk^pY@?Bem!{eY+f@ZdG^$lJ=vliJOxwYd&a!iD8&#T^AU*SvP_DVOYDSin z72RH7Qss1{;6DgyaHfwOA%A||B6COS3Ei`+m6Q~v+Zi;Woo2zFLB=X}Xr@fHYNfRW z0}~03WGm(pW+-~hy>rphMRdO{vzCIZFZ^kcWs23>L6_K^q1ItlzvMYsfSWLzY`UHx z;^SIz^ej;*WkXbJF@#xglA@V?Rw(T|F^*MWm)yZkXfR@?I;^<};MqAF)BZ zl!&Dd+h$NtiyGqT43ewOlqg-H{md|Y0%lOfy0So6KfR?pb$r3(y?`HJ&%GSz$p|t* z6DGK5Bdv+pRcD1f4}WP<8|9KIiQUbE3lubXT?zYen;erOLrk`?x(R-V-8#SmcZ~~~ zUK*27sU}GjYu6gn%9|B!7s4D!vYnuPm<5vRr~mW&w)cYg&NfdRwrYSRDgY92_& zV|AFBU{t4D^Dq{+A`@1JL`^xXtH-3)kVF+bzG@V^v=34=BZsL7n&LPiGHJ!EDiF=? z-8I9uWus0UP#z>%j^#F{TzknU2rWT=gC|+JK&(;BxroVrog6ZPo)f%=VGTebxm0@x z`f9_tkG2#kntv!60WiF8VW+?%U(Z3ib@yk|X3q-iMvh5#^CluWXQWEY%Xcsmv$Y1I zTakqS{=!a_XpZp|jhfK1gAFx;E4w~o5z`pbWEck#E~`ngi|fjtwTLj5v_^$JI#-%$ z08mMn2#xtLoF7*Nk{Z_)OCLH(ERmbo(Z+awMOmp6+JA~PQ8pC7UZ)F_m2^jo?iH{g zRi@$wROygzqN`I?}yS`khk(S0`=D@b8b3Ry;CrUbxBBSBFLTOH$#R5^O+*%D?6a9}rSylJ0 z=ps$3*+f0clnB%tEIVAWw9{2NOdTrund_oW(m{)ohiWcdo$gT5 zTgqBg0qD9Qy<90-@T_@V8S~))WdKm);ou zxPSaP+!;tChd;GG-2Q@R^p9ySKTP^%Pl7*CPI2Ctg%9ekKUn^{XTm;VvwQpYQqE1B96PinylS1Tp#WZ;Dh3M~PG5>HgvLb@6_xnUx; z(LkWh0S};S(ed(XACek2z~7Q09YL03Y*j#kK0|9aTsB`hI5ctONm3Y6(w&T7%6T!> z?v$%m(*w+Mh@pBNg*pGi?L~Z6K(Ts*Ur<*)dUD?$lv()s)mL`)F@C zVFkwOnel;fP?58RZV9DLNhJrM_?phrBEBqZVB1bq0mwb`RpN#`Eq^(-7cGM=lH#n; zP%7eTMd;nc&Ajv8r8Z!9HAiZIqG838qH6aZR2so$<0^%E*R6nU(W{6XZ|zE8n<44weEdOSF4I$ zw9?K^C~XTm7tZ*{W`AJ>iPzW7MfiPc@}#TM&p1s*E&te71nf|oKnN*pEym*!&x)lx zrOi~P5jQ8)97P4TbY2ffB}aps-7z3{LV|D<$(8XAn_SHgTnlk|(h`PQ*O=D2>X*$- zuZGpDv(5ec1w>NRI2Hw=fMO=HN2sg9uKG<#K|VRm4woh7P5i2{h|MzYI*f|VsmWtuHVEq@nrr6Yli+Ii(R+o@RB10@q?VXV zD(e|bLeJ@t^x|>ZjIE*UOxUhWDH!C%2#P0OCv68W^{E`yQU#^5b!DjKX;tm>;58Jd zm&9`%P3hNy%vjD0d+E+U=9&Id(zUY=PHq+(8pm`F-ha-o;^o-^D^yvmWM1*BuoM0n ztWK6XYx4*JW_PEt$OfH)J3?f*FdceRAyQ5~tdBxAN@)q($@SBp<(NU*2+&7IW_39~ zI5&AVKnn(RXHX0HTSq!hVWA1Er;k-s>T8H=-sFMxb>Lf3lFkUh5KP=Cj>fOnuyfCahgi+nHLk+q{wF!OrC_@aLqeRSdA^mxuvVCUN#1)*9|qUXxQ z!6f*FkS8bO;?>j~gTG8u#4QdDTL#A#Rs0D;}4##!m$ATwQp*L^6v|NMPq$PJg~xERi!OWKG}ZMY@mGy@hxEsLEd_ zj-svjKgol?f>=sQXpZ4Os3xhw%j@$>`6NW$V-uTDxKab0K}~9kF`E$*>6M)99Mq;J zJ5ufPWw@mZWP@0GN__s9v3RO^JdrQ80j=~3+3(h{G%+1tm1R(OhwN&H z{%9dSdO{T(R|g6ARbH2EfJ@fBPDzjd3W)OOXx`u!J4^x0RJ?ksy+eN!-ZgAlD)PaG zbcg(u;B!bhSGB~i%A0%I7d;T}9gBXm*ngXklk1Lf*WEu26p)*Ae7FmZOS-V-Km*Q| zV7)w3G~MJ2HRCE&A)_D)oEf!yz~@LRY6&o6FvWVJ)fAAJX{yAXfk`PMzs$>BmZ)78 z*#R`i?LrIm;@4k=M9`~~-Tje-21ufxM>oty@bHW1g;91p`X+#>S)kLB(;p~rw}10r zW&L|m#lSKM#j9UbN4-&3EbIbqVpsTdj=g_4a%p{$_yz$Ksz zCDr|=Y#CKn_(73^Tk^=ws1z{mOn)6pB@XX-TN2_45)PFar?m1W*ldB407KKOJVDT# zE`K!d2jZm<8S_TCUkip%SFnP?3y-z~25) zh-Wjl!%oD`Ib^itJ!;SoW%a6PlYlGcs*aTQg{6Xb!|unk@+YQ`Gpt2kL0*wPKhIwn z-m3l|1vFszih40YXqa!IfF6W$FFNQ~FDQ`EdOLz6#M>u{?l* zENPK{x-$tkd4KT0E2tNvu87Nlw2_rpfMc@OU16)Al|2nT!`}+xyQt$QyhohLQMUl3 zH_Y%K=kzzt{%=mx=nA;7W-Kn*;xv`%$Z>%{aOcyY$2F;d6ixo>;ynufa3JLIC;QCu1$)oc zEQ3VyKJ*gl;IjI5k;RvwWzrGBVZq0d#-Zy3T(ZD43Sc)zaC_;L&gLTwyPzldmE09`A2=d2z+ zOjgnS#zLt)%UwH$kbG-J!|d`=d1wEY>mou_j=`08gTJ>W6FG2EJ@@AS{X7=9Q@{U z^s`!p7_Ys;nuQpuv({E~?p8GL1ll6MWtX)>RHKiv(zY&hj#S5n1?1rSHAj{OSB%m$ zH7O~^_V6(3t9~uRFr-53gaJXgh%BG-n}3*CIJBnXZss{S27yaC0JTJFDtr@jx7Ds; z{f03Up4W~U*ZeILdID1!IyrJ0eyYWW*M;{2RlQ~nN>#}!8F*1@!OWY^c8#KDl zO(xYgHCLm==#R)3h?yTk^Z&u0^nb`z!i;5=URpYHn*DXBySU@$_Xo7d#}tQYGindq zVpLIxDGKdmaWC$mo@oVA9f%Fo4LzS&N1y}dHqh`e(+K^WO{m0K{(8RD_&1Pnhmq#N ziiZPbmXDw1!lc94di&aOEZ+N<;Sdj#IMis&%|+GSV-MvLJPbZ;P?5W}7Js^I0)fpo zhfw_j=jly{wWFj;ca4#zhcaMNAX-f44Z;}?rlNML#hw0Ro}5m?v1+cKxoVB;P)lxXujm=GUjY#w6SrVUuNh&YYAG0T?7qXLzxM!>#wVaq7o{idNP zf>#(VCH>h-dz2(Wh&Tcy5r68I)Da&Zg-7NP(Mb~JUL%uyk%v^yU=1^M_Pq{1E<_Bn zRwo(hL?^*G{o&3Jr^849{WZPNDM<=O+gF&_(UKt$zC8XKbc|)DU^@dX$ZNyr6vdtX z?ng()Z}ZYwonmPZ0hl#&*K|YfQ@OBN&M?6y)6E-V0eIM&zu#))4u8V8Ko>&V1Rasg z8x^rSQ3?;!{>(E;3+-M_DP=SQ_1I^|SD#t;Z|FSva37Hlzo-aQn0auNfsUZOb-mK!7ng_HycTjvQRDa+RcF}k=K?*90z+T&} z61y15jnYW}5(7k^d*Ck$M>p-^N`l_$>5L3!XS27P?-Nu3z$wjQhMa&POj|SE=`IZe z2kXWZok_AGhmHYzPlBU;fl#9|bt=h`_}!EQnOBTd=Wp3>i|_0K%2Cz<2Bp*l4y!$t zV6LN-cqTv2+JDFUZ0ZU}`U?{E#t{ZHD^jg8laicq4x@XxB8hUNacx>b+N~3fm7vbU z76lOZNcJotP;#D*&9YBeSY>Pe(0N!tT@roe)9i)q&o}L+W%0Uv#DJytn#w}xuU=B^ z%rn9uQFven-r|E!4I_##eLP4;Dj~d;pd%?^6=X2(GJk!?lR8U{5_y@0Ij#E^ut*=; z@NQVK&qfn)NL__6SwE6rnAE-V{z-c`1AeSZa|<|4$yUK;YZ&5T1XHz6mOti1FvI2T*N+0AJ@OeW^WEIl_bzAe;;|vef8$WWrzach;i|drpiECwC>BPDj z|CYAZe1GQR!43`pkN^h&K=%KkiT|TP)h_(8S5SG4rLjDD^a|G@+1wPe1xzGcGXZ3? z(v#PjBKjM3?Ux}9#1=(zMKT$iNZc%?EtiDeQfnq z4}aG%^x}`gzu(g$(nE7F7$2u7X*Jm>PjR-IodX?5*H)dXNH)-{AUV7b4t_jOKW{!B zJ`Zk=bX-4g9xe)*y$1dnpH@BTOr)apVr-EB@Or5JQ%fn_W+U3Bo}5?&b)KuA?(O1b zRpD3JWkD5^*lAO7Dq1v+g%@pBjJF^~C4U%9#-iIa2Wb%(Qwsqhg9)m1m9gOBx=MMb z;w(9lqS88dtd635)Y6Apzs73B%VeBBl59@pI;6tJrozlxw|;dtQN?7LX>u-LQ)k(_ zs;qL;^PmycGU&CPHL*plwZMqF%FF#(s}|QsypPlLWrqStYp8{Vf-p;&%2nk+FnHP_YLx2KlqZsA=VoV|$uCgd|PqP&*(z6n`nReFTL03drSD<_&cp1`QaPWDT6#ZUzVT3FuutbA}VIxfjrYs^cv`M z$Ms}zM&@!P=8jsF%nhtsxria1@_-nt z?@&XL-b6I>tF2R3aNoWx=6}|ERdip&)|L(3r=1=2x$SKZ)9xT{C2}9-VvcJ$d@MSP z^6;MNR}eC5fWOj)Q%H7%5~>(0_2v1R%MsYajV%XrT36-i4X<{TUqzlr+*>-_m{=C{ zi-9t5wu^Ng{c7WWR?3Dy(OjuZWo5&g%lEg^k(n7bHy@7m(N0Kfys`pi)hUm5>{HbOcgYFgc{D!7xJ1q8g{Z7QI zVs^j;`tmGib&N((_kYK}Atxtp**686rA<12qAw%GJjdk3mEbegw~Ym9Z)l&X#+63& zVb7)f=nd_^2$}`#vlP6$f81d9gE(LT@e5V451W7PYh4rlmYF5tmhf5>u;vyn{W3u_ zgC4Mg*K}6m<|!6nV2J+^d!&ruBRPbxy*gA zgs2?}+0@J3aJ6J@RG%uZzse<6K34p;GbVrr6DM0Xv45qjjj9bAP$ z9kg>~dw(vkSVR^fwWdqL9()(+Rc?*;)ez}grUX{n1BmA1%@4TlGtUutxc!?a*F<^{ zN*VG7N!jVH^NcQhyzhBlN-hl|>Z^k)->|R2wmq8}<=v zF;zBiUo{bazLS&n)$wc#hih5HJF>~MB+&y(i52WvgxZl`+12op!c_zj-Ol&(FKU}a zl*c@+@@M~Ij28gzFYv#b-eCWkt3eO|03S#I0EYkE^eTHgyO`Q4INASGaj`UYmN2w4 zv48pByA1r#mq6<#))Ugu&I7?NqD9djFxNz?DAlKeMLd!UQoIz=8D`N0@3e0uAF6-* zT_+vM_}d$_!-&Y$325$I01CJJOBv0H8u6 z@y`9CF|5Q(cNmE><&@AKSmoD^-S6$89|>RFPPTx*YK)Q@h#GME?&L$r`j2X+_YDh*51bdX4;mWe4xxC1!oBtYUh&7x3hkUt-Vjs6n&3}@Y z$29#@#?Dx@4ZS&~nJ!5alD4OOBQzU5MvvUo1oX3vh8t6EbJI!--hNYytZ3`tU2S!N z{nRE3d2GHZDO0j-1}T=rwQP~vfk}m8t$q+$Oe^jgrId3_BCM`bzqTPdw=O7Cn>sGFWSZ4nilfs|pz_&%{_d<_w_xS-q!tW9NmL~$f0t zYVAiDs&|o*vUi!0v-XlhXn&uILu#LzLu?=Fc@0lDPoPOG%KTgMdq!EiVWG)ZwkUUR zdWzHY)>93dqE!k!KRy`sjgCoTVti=H*!~9IwVx~s#9ooj$w_sw+YJ&o0+)M|7aRsu zZp4aybo%q9d%rQNbdO>o@Boo#wF-kuC|bMk79aY8eYMe6xPSahmF>p>%@cm-e#4f1lvm4D>Ega^%e|zU$(YH z+R?UPBUQ);64M9spRjW!Q#?0~eIL|!X43vAh(~?c{f+{^NMm$!R*N(FarMAQ7+S;F zz@oZ0yx}{#;;hD1R(~+WLKSF$=lnEd+|PM=l%Ns)dnCxO)~L)A?|Ye-(}Qos-rlaR z`vnE=9%$2yjBn;|V9(_T^z~4#!xX7`Z-49@<}QUa_XUCHg3}>S7A8?;ycys(hhsl~ zJaPAVA_nIAAhiJQc9{}cgJJCFQ^)EK-ng^d{63b8@uT-}`hWAHS7fcvK)yBCz+K$z zX9LT>3vzq99^>NdV4Cj)vz^8WLl}a*ZN%*SaYe~v2V;;pqmnEgHg1fUEZ;jM1jFTa z-P4ZgO^8a!gq+eCe`M!q8@;kJ{QEh-gqG_toFcN1)h8*N5m?8uh|URaXH2seB+2Yz_khJQm+Wt_YdKHf=!RCfxrt)f#;Dbro!t zdFO@oc7F?|QBgjsOSUSOwT<$v=WKv5NXWg?Z=CuG`el|8_@>GkNw$#UGKQYJ7P*?? zoIb@JV_2^lzCO2>v-Z2zdAq&`zIy@c&F=1L748j3E6Rk1!^H>eL~F}hOEwO_+2$&0 zOkU1rZZqa9s`c-Lr5leNO?Z~w=D=r3lDU-W$A7h@z8w~w?k(YJHVYM(){o8hiS^`1 z<*CqxRh^ZKi~3_U1vk-)o9=t~UQ=<=<yso$6cb)k;mzT9$nkn2AC9K)O)p!6%@73r6uQ z(|>cy2bhWB`X8PQpbSC@?D!%|<=+ogg0T@|8@AZ%Zve9Uy2Qa>b(~KNA~D}!w9o2$ z&V#2xbfG*Tu`_Y*z$)Eo=4bYqy#8sMU&`TWn=h1R2DxCzM{f#zQSD&BX3ucL;-h#N zvnYm8et~=k1f4MKb4;2U`sEL)7crB0J|#lxJuZ z-f6k~Q#pE!W5XuTcFw=O^Wtta&3CQ?HBoe|P9`(RlL(s5u`2_>)%ftf5k0#HCEWGl1Ze+#sTf0e_d{^JJVpa1|+{I}2- z^)NMd{ok)%SAX+IKEwRYrTsWQ(qIC!fk7Po6K?|p0zqB8o_@G0++q_>Si>o8{#Rg? zvJ812C39iXrI^|_Gyc|PJ|i8?27ima0D05puII1$@zSg3E_ZPf2gSGGomcML-|I8p zp2s_MeXx4a-^7RTXmMc=89~%tsv}#dK#nqj!L5&DN6b5jgB+&al6{!nntU{Qiu5pN zrrja~!r2w1GjC51eE7SwDE$2c$x=UtVEBhGlsunwb@{M+n7K1g54PaA`+qM*GXH3G zeE0)o9{I~>O*wT7g9Z6&fvQG&pGBHXr@w6 zzuXD=@%C~9?%v7cI!cKnq#!*th9FH)_4^kDz2y1&K5kBq`5i`<^_@L-CYE{TM2vHJ z7g!iLX^1zRH94yrOj_>U+<#_TV(wn;!7n-HthSbFUx_Ic7swb>0NlTcx_H4(Mn-%4 zg7A)>n2eqdd*i9LTjGhrJxHmz7jn@g;D5n8sStUSp*Tp*GdOn|VR`8dD~h}Z*qCl2 z4mm6O@`qjf$K!vXapZ<<&UhV{i>I&C8R-m#<1Ay^;-EvlyK|93^0_2!wGik3G8@==momASu zS`=4Vw~b4irwM16JnrxscWQMWDK8Fy$f#g)@|s(gBlKv`cB?jwO<=w2f?rUzKD+FO z2qBSltKLXN*DjKCoqu@|>d9f8e5P_T+F8smIA<2$usx-!7l49bG(&?$7hq%mRNJ+| z!@4*NJemzir3k4OQiy^+GM$c#NUtpdE#fpXxeyv#T?CWU3W=p6X|9n)xy3K+Z0HQQ z#kx2IKctZ8IwSWD(mqaK4jESo};^8{avuBkbj-JWqtHCqTQ$>5k=!}X)i7$VrzteocZl?8;7!QkP273=iXv-?`l}S zRe?FoBH66#%y^~63sv#K-YXc@ZS>5KZ;6rY(Xc|&(#;_vy&uP*MAqzM>*Gsgv&>?> z3HQsaG$&4xg{<;V=1z&cJM^Zx!D7whuEs;H8@f`(>3_FY#Sv+PigD!)ETXDC`b$D` zL%9M+TUYfCsHd1PCW*>#B58^PIg_jKKz#bnhkxBuh_^?aYzm~YKh8k%JxD? zoK{S69vhs;_;O|n0{)IJ^G=F<>C;K-S-(VMvN$NcT}V{sgJYGEG+2PWq1>nB7;XE_ z3j7P!r+@mut@n*5UZGerm1z-*N`{{o;!CW#9V=N^#_rDJ8z=fDID-E`j_Q9ZgE6Lv z>96>p{v|uQr}_o_n_OQv7acAVm(6q?F%IcF8R5ZKRu;`DEH55iT}R386qINfqF!*U zvbrkg{iSralr-hT!C{zDmi<;Wymcq2p`uxhw14{EYj3&}=w;@bJcgCf?AcJ#SzB=C zqSW;59n#jt6>+oYcKOYPJE4_c=VO2-KBdobT3O{0%JEblS=3v2`RF&Df{@UkjCG(Kpwm zFZLI1&JXm4oA>zlYSIKn}4-dSch>`h8M@olcXEGS42~sTTXca>O2Fu zr3akqP=mt@4UjVg++3j<+GF#dtFi?{oiLD?N@qxWg!vW>3ZPhFwC2@y7@;|%4;-to zA*p@RY3$lq(mH{hk*_Km8a28it#J(jW(4ABj?DSfgq6$81e9(iWZ7o}E}@dY7k@&3 z)6rw`h#i z=@Wktc|yz)Tyf_EL|N>JtXJoMM}KBCgnl!y&*4o$wkS%sBAnnye8F$WHp z7P(B;@x=V&A~951*b$zMc#jv=tZ~{?n9~*5f!Vq=CODac-7GyQNemG&<$naX<9|Ue z$UGv)ii|qFKpoQY{9#YjtCodWtg=X*xMg3U6DT7d_N^|T06v5v0y(%Z#kA>#BSqU~ ziJ}cVb9k%Qu*(@~ot(ZTMg3F|he~F$Zm9~)(saekfbu_YXUZcZ(r$Eu+X|ZietRC( zHm?uzoEX-+j+L4QA~EnGfUsVPyQA&@joRF!uoo{>G)StTZN(Y`R_sN-OW zZU!lm@Dx^=BRqT`mv9b$&MfRSelDU1Z-Z-_pI!~gR@qk792?`ogXqAEX8;`P*}L6^ zfBc@DI?A0II6OJxIQXOz1_R}Mt%-f!=>oES0Go{FMURq@6OfCKV}I6=>6i}J&e0yx zfkn1el7J+JRyL8r;N+2TM^)M)3ImOV3rEV~>(v2%ZQsC(94P@nwJCw+lt&AVL=vV5 zr<;@@*DVGPR=FW)`6S%PCD@G~u2OEUL`pByoj$WbM`;nklnH6_RL>`LfMtaZxLCD@ zXqBtuiQCl3M)K04B7b#cHVj}ukx_K_2={RerQwJ=@kmd+KrG}xeK3|*OiL}KqphXu z&LjePH74GbF$YXAfvVIO)fmk%pmofl^S!W|I@9a{SUW__J99zqZ|xfEa)8m-zW<; z#pIlnEFER5EWMP>gyf3s?8Gq@)eQB-v~0@>(0{YTDCPhC{SJmshPI|I|4?@SoSpxV zU-|F0Duw@7AJEvw^8ebVbY+{rmggUSWzhfY&7l8-UC`)%Y)xtGB5ZGKYiMU8V`*pl z}G^nY(snSY|9YmX#|!V3%?b+t*<+Tth7pB$$|C0~kwv}nOXScAAYJZ7l5W^0fJ zBV6P^xK~KevM`LlAIG$d8?TVs{A_r(%jtdB;e5LLKNfubfGLP=2mo<&xxtFm8w-p* z^?M_FXyS6>dg6kHdaS(YXc~u`IhVm~ChRp}s$tifUu51BQwv)zv>BV{H{J9=~o3$mhRWw|0pMGii&8zc4 zF0`srtw7AOi$M$>vN?**ds0gCmb<_W0jW;Gye{@N({4b)N`4f3GP*a2!Ru6}C9kBQmHr%k^&X3Ee`fSBtW;EPq6aMVHVp1IL zjX;8v*&dP(9fTzdCgiM(F!Y90JYZs0bUlFaZhs{&lPw6HhwyX4U5Veheql!Nv1DXr zhN(k?v$e|=F?BO5KcC$Tl?o|J&Ge%c_74G(fcWMnGO6Hf&TOtqGBq*&fUd_s7I6$) zZ3j};C|JP(H$atabf+hzY>eh=6Y~VzVQ<9Rn9&dS=@xkA(8m#+LWd?EeiaM%&r{PN zgnyotHpqF!u2k~N=B_~TxOxtyyyN3VjFdYB#8a|!h;2Sx&(1c+`L)3MO+UXUdKDR>-%Ne-4IrL;+KUVT-BD={@yPj) ztNubQzDo>@jXxVePVuIXK;f%(-A4f7^W6xwf(Lo@N6fv~lNsk+`O_H-kD|j2B!63G z?BWFRBk3B)qg{6o%%?HZ8h3QJS#rQP&k#8ZVQQ~MT4x$Nb&e)?+b)^5XEfvp74BPn z6ei3^nHl*lz2L~cso{2+aO7IHL%;6knw@lPfNpJ)mmZK{f{Z?U2&GG5-cR3Ep&yJWq*hg&oQi;&8^@znKfv))zCI6xIsLs9J8z=&#SSH zk?L$C4kT(Y%k*mG7T;_tnoFuDBs5p6>-Ia8fyOXo@E4?1a}`}@j0`8LNr}lXpmZe4 z3L02KE!H}=m!eLM9tut{>Zl7qwLF#TfL^x>Zb$wRq+}(UNIGH(EI2*ppnq9GJ{D`T zs6>^bHjjnT8~4iWmBt#3jSd4bd=Ox-%s1sJ%r~0}Eb$~N-|KD0<`zNXCI9)03yhRW zoy8q`OIlPz6Z++ks@|0-l><#LUTt#IScLs1gV(BI4$3oRTj7C94D(%fQ|SSFL`B9x zcO;E!Pe6V|jT$G2mE>cN+=o-bEF;u536^+TB}I7+i;$KNwIo zD4YTRH#i(KbMCeaJB z2fxrh5nm`FNi>g9R{%gjzrQcIX9g#ft_ks_fGos|?H*6MR*Mae=b?2`nOZZ@!fy}D zl`czdmqTO22%@daHS_I*siF_ta^*0XzKDMX43s&V@xBuks6BY<56MF>!D+-M&xCoRC$74cm^8^v z*@qR)Jxnj`uM6nUb*M;Eat%Q}N*zHv3b|5kzV-L}G6D&fIOI8oFTG);+%d#x)oLZg zuvMgA^zmo;w}?d6!>ORs*Yn=_Gr>#;27g=NEmSh`GXsr2ci`kbE}DO(W55BYPg^k} z<+8Tmo(%!cJ07qj(cd_R{jpDY4AMz_I28n}gh9XyN6@5chHTr(pY_cwQo%FR(SpB^aaUm?f*ob8C^Oty8SFNkxWTl-}6`+E3&QVrkbq z=ULaoZ3M-HpW#2z8-0KA@dw6z#_#Z(os1RKn|EjQjYi2#g0RaG{f&Nq5`G2SHQX?AzbgF-8BeKP+vp>AWI+1h%*i9q_1tmXDg7bQZH(5;S6dxw1+Cw9M%p%BE z($o-=Kbe#PyG}>#ujq}-{xRc-v5=^Nv;2CZ^?zA^>5wVoHV%>!<`rX98#n+Y1pjLpu{&~LtcdF07Jzx3%1L`?y+V04z7&}R&IV>4^ z;mv=R1vmO|Jq&G|LkT680N^Q&l)5KAU9;H4Q184ied?qA^D@1@=M1atwano}o9 z@Vy`CUzl`h?Q{2TR(EAZ~lHip#4+tWYM-9@ZCnbCjlLl*QqlTenT5?&DMgV}YFA&7&O91{@( zD8hQ%y<}k0VuGQxf^1K%cBfECT&1<;Ybz=Xt1-*jW^qy$IZI8PD|A|!!8<8s#w8^M zr;QbyTZ_&c3q#!n~-Qi07Pzm2Jy)AX0Gk--I4zcSQWlUi1z^GcS&@-bOeRaSJ&C1=f=W8p{k z97QLzv^ASrVnS2txl1=gNJfr%AClTl%cjEEH~R4iIPA=s5~zk|kyTTftn{}jjP3CKqO_aO(!kIlQ-muvb)#V|^blM~^W7g%1eF-+Wl^x{g%>`B7huBKW zK7LHcZlnAMWtXQf9@GNK zh>EePNI-u*TycwY;M6@hl%hHtU_>#m7>5|_-;w80?#!=jA7V=}OLEy+@*)eD0-W?6 zIWMksLe%9`u-)5J>9MVLEV?7Th;WG?egjVCy=G~&EN_bGv?{yAtQGR9*5j~Ad^ZLy zTk3_Vp<0c@qO+jv4BG;Ajn03^;6U#VS_9u0i}rziC<>Z7VVL-o?iu+E7bf$Sy#kse zSP23HdpraONcdFaMY1ot75s+is5nBFpk7npChth$`G$f6=N>}Z|CR*S-M0jC4?fSD zmy{1aY@ASQW&Vg-??u1Jl0%sQIAiplaW4; zjQw62LGKvU-BaE-Id8MTbP`cebRT#(Sm~|Z^Zd5AJzV93Dh^Q9^XY)L<0A||u6n-{ zBjaS8oZ_HnKg}Y4k>-DV%iZ3L{o`xvdB)lt@~2OT^?n8=@d~i~ME<(|@610pVD3!_Ds*PC}h6&O(2b1SNMNcudA;nmkW% zkUT{}_#;-M+*he!jih6nMN&KVXuf|26~#L>Qn9^p=_TK1>28t6ueG~li0j0&bAWS9 zMHEZK{IPU07@T$ErzgiLzfH_U54}#U#0g#BI6^%uH$KG>H#7_879vj$7hAmn@o@vY zaGN}H8gC&!@Zf)qZuPKHmygFT`3xoaj={^o zuLOLE?XcT9m11aZyQ4E=S$(1%oS4mto9{};L9pY!k9L1=A~N6+3>1UFCY6AvB!MB9 zBG8NR(j5}YB&h<5br~%<77SYquwb6kA4|R(N94!lvR2}eJz|DEC*V>=aLU9Ytw**S z;K~?7d);-94|+qe>AV9~KD^B8Ep+V+2glt7+l+%6*MxT(47o;#UueCK$ob_`2@aFXDt4w2d_0lvt zTkYCb_4O>7HBET*stecoj>Le~PvAUrOm`P#Nn>LK929qj&XXl1Tdhwq%ILY7HBKjY zu1gR~jgw-WYfJxIrr@VJEH~(_xm(!0FA??c@sxkBo5;G7f2b3H|7)u1->is&p_8+z z)Bk!0UP!dy|F5;T0IO@swnow5?i$?P-QC^Y3GVK&ahKrkZo%E%U4u)|pdkeQbf3O2 zedyDt&%OONAKzY!O^va3)vT(y=BgU=Z#mmVUoAd;f_6P<3$qH+%cFpd2qX_XSJ0jV zOE`bs(QyYtAT4fj-hsUdMYw$ZdnS@04iQWvk+A7QGp8-J2&je$mgt4St~u|7r8?cE zuXIrJ(NoC|$#e)Cda_7FWE0V}qL!w48vEtQ{5yjY46BxtY|3+-uEB+0P^3W2Em``Ul~s)A-e zeG)NCM<-`x7o&fC{M)x5RQn=_qKNn+N6A}DtDt~*8HSce+X#+S>o1fXi3%+!d9~f7 z`$cG#bY0XNJf}~+Mb9^Gxu8WhK*~|CkLcMrKEHHlz{aF{rT@?GpVDO#|6*iFgJ z5m84#<|K8zj9xU!Z56HNIGL)fW=eFR(ZHyu&Z3G_f??D0>a!h;bS73yP5l`a0M?xd%T~wxiBAkEfS)@>N?zp|fX;B#_bGagey4#$n#JlZ|VoEV0 zdJJf2&1eosSe3p!j?OgWacFgAd??|>>ZnD@Tt0?N*RaF@CZUOfFMAC_6W03)n|Bwg1KBGCx$5l*g>7IaVHSLx6zU3 z+VEWit`pzH9;NWd`9+2(PGpa2n-5RbYJ~5(D$2ZDBCG;eh)C-Tfju#%-_ z38-ilX$x9O!cqcu{L1UJjOu@R<<7?dj!eZLB6=Gz;10T+(6iFPnvf3CGDj&wgbY}u zG`UoS4R2RX+lN~)}nw=9c3CZ-6pw4sdx&HriPkJi{g$iG|L6wH!0O{hC0&N zaD76*6q5C+QEPlZ_25P1Cl5-;*7EAYxYLlEAL)Bcd;7)Qx7$L!u{?iB+L?YvKHk-- zv8G@Z1J);)8WZ}m;DYyD*P^6O^LT~kbQQP58Xu433jyBh9&$C%Qp6MdC}wpcJI)y-#3musd-!?p81kV(nrTZrHH(IS5=k%D}XpAId=za$L$ zlRo;JgX{EueJ~`lk1w3(% z7hOPno%_Qzld$7x1u{urCA7~rLDj`}4U&}FyFr1B>7;UoNZ9%h zE!m>%uy}v$n1{wER=c0Bw!-1n+1*zXA{a~d%+Q+|rWrgc-Z3TU9BE0ZJX2VuB#xv#8mCK)X#Ah zOX3iGepe#t9qwe6v0^EmpcjsVvGFd`F%f@lv9zuzhGxDCB(_I82`d$qY33zp2hSZ9 z^IpQ%XjyKbA!}#4^=!4_`*{g(#gCgDeP2+9&`>5h<7+TDX(mAJNPHrpp{5x6$NrYU z3f%V*lhDKE3hiXVLoP8mgd6)Uq8!F&$>Rz;zW*3(IHT>=bv#3;t)2Ky{IvN) zad~>O%xoQ8L&BFNnQy|EwX&Fj<+?}<&1(-(PtzRTz#_a)pb2vz z-gap3T_qr@>ArwT_YKhKD}JtE>H{DmfCAPAr2GTgrXj~R^!#{| zx|_Br1Y`;o&)ZIJB0Tn9fl5PhtUzuFs|rE0Vh$nj8aWI)uky3Q9GcSc3MARaD7-RyyB!r zQP)Mc#dgKPvJ3d{nB9MY=#j>xi03X-?*_=b#GMfwDJ9qK18xg6QuNX^X@Q)wS$+oj{QlwuwI)NP{s)DyH;fYKukaipA&_HQ6tQbbSt33#~Q+ zNd(iZEVe)sNcB~MDjYeas3wfI{2;|_8yCpVq!n2YT@o zV#s#HCFf4{J%;mBkGak;?hT!n(I3El?{`{=2XwuiN)Jg=VGk&36+W8U;C$Hdk~8P` zd%Z&HA!CE7RireSpyAwL9p}Z7ChN2=Ff$$;q9W`r@z%awav*FY^B8 zJ+qzYDSqaWN!c72bLASMc9==lFiVYwe27DoB+H-VTT0|YvL3JITZ43q5Q46l3ne%I zP-N>)1WSs+V+r1Nbl-UFId|?pW@@efs5^gGG?TZGjFjXOxEU_bGc@P;MF1CS8O3*6 zHtudm=F@*DYp-D;h+qFP7D%S9U44D^+BP}jGU|H#afdG%yz=Z(TjvUQTvjPxW*M8! zMro* zG@C`>qS}x4V(UPBv4<+!F2j{E8hiKs>Dcde)#-mMoS<((Gy*cRV3SCu_|k;j;{I7c zdOnB2r#FL<-sBlpRs&OX+IHnR!~xCENmFBpoR8DnkrR1X=bX3Ft)ot1pVcnm7W|{f zB?*vB?nHc4h-ICbbBeKt=^a?wd292?BwmKkeQHBmYMC|SEt&_HXh2tQ-g`zX+AaSC~Shza4YCOuUt^Mr47o0(ntSz6E5~X zQ<|E2_tW!30s`XwD+ViVYv*SBo9Fl2UzC5H4V_JYv(rh96E_r9)Yne7$-;SH>3Awp z2a)3h=YT+NH7n7|M$syCYC71_IQT+l7qhuQoQw9b2nj`dVFwHzrOMBq8wjs}qepgk zj?+%lYo%WwN1AGS8!9_~o}o^S4&fwZneqC7W93xM6ETg1gE*^*F1_DqTC9rS;Y zJC38;B!!p)fuR6VqAN57Ali>f5eBs&WHe&f35tsH7zJc}2QLN|Xd*F<7N8$YMLOIc9VkBz%9A@b4il-;taU_Fw2 zPM;F-o)nUX3$9FFcm?emw-zXzoQN?XPGSsK-N^-N8s$@s5IrbZdgK=_;2c%FB_+$I z`g*Gdw#rY1y2OifrW|ye$q(3)%1-lajCwo=o<*-&a}y@g25vYF+Loz+buxeZTwj15 z^+nlkOj_DCoDWDIJhk9b)*uD%NpEzUPel`>Xk0lRb#>7b%lhX_AWTOHSVz@6?0IZP zpDHqs*RQ+KsAQzmqbaMlzEL; zcHSqok19wSTKvXo4T)`DL5F|)G?Z0#E&a?f6roC7US$)~oMSrUcr)jp1j{Id^*ESw zJWdiZ&IzbVItUCygE7E;zrP;oIGma4>7`ifj*TVzkEoS~*>R9v{OFEu%Y-z-W)(+!$J=Ub@F1QtQ34 zmTdTV4PDVJ-CMCjTU)=Wa@R}frkx^#H;d~s(gcVO&Q@U8Y{KRbIW~i;p3CENy>oha1k*`Fax~1FzNF5oo{U@!sLQZc0QE{QGrra5HL0v>R$i z_ATFGfd&`v?MRq?boIFrnO*<_y83nYD03-VF;` zNs6po%4}qTt}+{DAzxG(f0mRRPlYu_RlPl5oNQOp^JMw(joGpjO^c_RdurE|IzMh&DeysG)kpXRrj`t+ zLdGc#u^}vea_3ZXAZ>%}-JJj7l~xV8dwZ&=sSwCvZwu=rJ=l&rLl~BIk1-E zNxsA`@_>KWLN{|ko6x7hJIgrYRreW_>Zu}WhCOvrANvRL&Q=Y>cZ#VeIyS)A#9ijI z71zhXAw|_dmjR0l!d;D1O-11?e|L-o4uSOUSS<%cn_Xe&h*%z&a-S1T7|&YhxBlj0ve(!?#^0l}KTQ zL5ysJL330{z4IODWdB=dsKQTkQxfq4Yvijd!B}#Ej!{Iw#JvdMIeHfR>xjK_wRYl) z?1HM>@ki*+B#|(F2by9^R&Cz(;J4Y>{T^F}!p8zb;78_x^|kg2L!)1gZE?xlCC_-c zbSQtWc+u|$(ueG4d;MO&uF~L_WRC4*W%O2@8-_rBy7=7fH&@4Vw4hdxyUTGHVTXva zt*qkL!@DaU6_!&g{T!uu4bz$6+wXAA?ThU9S>2)UMm@>9KJsf*#;#7p-hM0on99^N zN&v!oEvS`)avV4eVocUP`SK*WBHd0(x1xU&a)cX4H?oRF^Shlj5gE!=0&(ML*&AVT zLU?(o73ypd8$LC*1CtGO)aaNhe7%(3M{V0t9sX?l0ak%33-(S=xV^ z|2A0)I-38}OqrEzBR{WzI{fN}v$=$ZMHLn+BeDuw4;N2Z3N;mqNuF-N(5sHIG?9LR zZpWQ}9c_0z|0skUGJxR=#GiD%(KG=p{cb$rB6Hoz_o(hXr@Px1G<7f`2F#HwVT4?g zio!)p9GZP`H7X&(A!#qwbS9H_+EIT5w;2PKc>5h4`rImCs;xz6;I|~^%UXP^qX@)zpI$K4mZIplCHnmWa zzC~QI{*#+`28InC_MobK!n%@e7U7tw@PXStS${4uJi$^sL7Tw$FTP_))G;dv$y^NE z5Sxydc!vi5jl^6s_~BfO*^^a+jKf#OHQ^8Ul;5?Hz9}euWa{5{(80V_Eg@x}98OtF zm?dUoF2{NYh*SHx{?0j*i6MWM5fqIEvn5RW-Gu>9j40sSOG=pG6Uh(LR9+8IEHcmL zAj4D4ekkm$+KnIJb3ZKYR*RvP$FkkEox9PhiYa*jGW6yq$LGm~57@ktAFxR*!%CrM zM;R>TB_e11sHX1`M=^Yf+Jrv@PY50=e%?pd@%P?p|nK4!5IBQ{Jd+43-X@BAPyeV$$B1*Yo*g?(czM z-%t0VdKh|&1eIIRh46n9Oj#^e@K>htOw#etT-mJW>^c&v_W=5{NtbCw=>bp!796CQ zjCbJFhG3`PRr}E9$WaNh9^b?WRlubA%72>wz`MWbDgMLm-3%Q~|HV_RQeC&5|GSon z!)^;n5}VE#X(6TTru3QsWv4wQnkZTx0k|lY9nLV={6O^*e29Pi4vhN_FbQ;+lsC}( zkW|^t1S&x~H2STqw1%T7bF-g4pz9S#VW5)(d8BNHfy!-J#WSjG#8@XJ1`0fp3RQ-h zqBP7{O;EAd1rWT$lCe>pOtWq>l7g#9zW`ptIuSTq3oYMjKe=}Legau|!OOC>H4Xn5 zUgvvm+_C9+QwM)HEW%CimBF`bXknAZGR1nylNAfriemSn3b?PZNw`7mR5}`S_gP43 zbiz-gP3D~UWAGSVTC02bgP&a1*#&Q zJ`8!+{3WEGoTj~t& zxSTZRVLEBF^YV#hSt((75@0@cekN8K+tx^G4xX(+UkTu1T)@CVcT0uzVtG`tR#>PH_n!O5{v(&5{J9$uxqDl_JQ^*tXfu^?fFUd==licCF%lShmmQv)rqzFGxZE$)G(Bzc06vw2a;-Cj-eh@IeIIB@&1S0k z5EXHb)nxv3ST~#IMf8r)7?vKBY_ELCVGp+G75_&!Ji2R)VT<+`i-^79K&@qlX9g&r zNKo)18`uwk8eOlHxDdla-fq*`Y@s%iNxtMxh4ZE@pU*5sPE0l4yt5^XCyIYm&UL28 zZ3WU1bOOLoe2yk?2}KDeB+AU@65b@F0fe3p_T=9IhI0pq{zw~yqwbOUpAke-h+3Jv zBKA3ho6lg&{)=0KfG7dpEGefr$wZ;Fz;SXrUOcKiz_V(ZFpl{) zi`%}LaB2xK5KsZ=zi3`n) z#0x^GwMc!#V*ZmrszTQqmZtnD(085iy>`Vuk{Z^OwDO@r)htK(8TMwa$@e02NMfxw z94G72$w(5qTdjP}O)ypz$IF`L4# z6HcFps14r@0&xcyoID6n@axTLK1kiW_dU$(AQ^gnr3gMHNz^}hT@Lk%xZ02*0P~K} z2zN1i#oxk%z&CS3AwMBMqMc%sf&pz@0-bqiyu!nKgfUmIeAV*`DL;O8e>=@glLNx z3ihyR;2OACP(vtS7(e#e>~PoYR0F(Vq41Y5>&MrhiJN&YmMcGTIR97!5tg3`+w3Ws z%>UAUjoh!&wOO;Nq+zZ=Ex2fisp0BlZd%fH5^VQad=w@VFxlyX-zH>-wdg(`jTxny zRtqJ33bTLh))z5cFp{Ti#R+MpM!T;%U1vT0iAu0@>PNZ6b$Vz#%AwqlGVn<{3cphU zV3@z=itC^?mEM`E(U11Ie)8pa%)@urT<;GSVl* z(ip@W?)z<&a969l2!Y1dntbLO!o=j%?@a{^!xlMRn~gm8>u+wjo;@a=j9BG+A)Hhn ziGIJMI5foVblRf@c9c+0BqAaBaS+Z3x!F3erqiZIAc)62@M=ea7;TYf~N zEoOhEv^xt9yQbc5t1 zCHMh@ETjDvyG`yoMcwaAzgCI8CnLKMjr4yPt+1C#hhT7xt*|1(B9BXuEsQFC7Nd4g z{#p*Qnn{N*w91Vav(2<&-G<99(l1g$h;2It2-Ag6lrT|h2`OVUQ#r8}KPEOqLDmw~ z8v_wL@J7YH!_x_rW6skjyTkas?VAfa)F=J4eRuHxl{fsy@%=27>A%JIU(8(BLri~R z(DJCX1S)5gOehJ0lMp3P63A3#K1kfMCCO^)xQ{_j+ zK(D%<@!&tqc<6d?G3)mBdPV9XQggmF77L}rfb;li|0)xs2k`)pR@tS6+T6T>IGtu) z?hONluOYhcKJA`s!wR#mf68u(xu<`5ZC!DqVAi?8?psCcqTS;t13qi zU4FoU=RA3c+bdL!n8wWX504T5fTRFY8Y}mS+SNp3Q82YdI2!g^;qL>dT}UXr zi1mAg0g5J=bKN&)8b79CoQr=n#AyKWUlxo~Pk+eIkUG#bW8sTT18mn!YM3>0-AaNC z{q@SA=|YMu;)oJyh%-_@Evpk;|wI6C@;~5Dqkde_@sXUIJ<{Ho@ks^ z=7w*-H3s>853;;ya0<7qvCv$5$U4#}?FL~iN-Js94&CAeQK;8`*?xc=d9>_3+Pg26 zo}Q6ISh2&Jq#Z(RtlH0DHZi_O$5yYRi7KH&T0@Br@vQYc=t;Wt7R8_~`#C*6GP7NQ z5J-HXq48Nmvh~JEm*RiG{rj>K?rpKfl(}Jbm%lf616f~Hu%G7M0tN)c^jFONUuSsb zZM!)I)E7BQR2V1!^#x8eBz8=k#ZIg!GGtkq5wpP6{w=zhTDpU!4Og*Oa((aF0f<+i zSBkw>+Kf#C!Su!>@$Q?f%gptcmzx_5f6zu76UByjVcOVXwo`w1Bg0{#pg6A`NqSGo z5tj5}um+qcoxTs#i5ENHOg@(*7=3mso`V7O@uQE;U$a<>mFP)ObYM_9tMVfwkw;z@ zI;$>Y7#@EeccoV_Hm@8Qam&iPG54hIi{;U7I_3UBZMq3^HJS7XTp-H^ok|Tp~up!xQ(~3ROu^1y1_$`6RY#K=hFha8G{Y4 zEE4)xu!?Ic>x*V>bS=@dJZr<8+U5ysp0)0CT)3$XLlJ)t4d3(NwcVX+7U`Hk^VBd4 z_ww$oQ=)ebW8G@-PxS`JX#Vy=#G$lxlNT@k*U@;OZ2V4KlBB3B^khIy75<}A3NhhS z)+h<^>R9P*s-xba-|ny{wPYIN1(4HM+Cx|oQL6XkWV|slGRZgERhA=+yK1P{r6>!6 z>kBC__4K|yC2%PGmX{g{rZBz*w z{zQLzlc@w(#^%%3V%@@m`)6P^gJXtv{LNV(@B3w^ozJ@Wc?4mG15Tc@ zMEl$6$0Kd)l$fkz7i5c;DbH4tc08$vQks899OVn=s-`5J&Oq0=;TCMt7|E=>MdVg9 zQhMdB)jlu!rRv?BN&5ATErDf7vME~^g9GG2imj@+(Ae!Zm_6=Ci8=SY#qoHLO`Vj* zF?PAy;%D;ml!p=D<)_Xw#%e3hWMuz1Dj{T8L|q43*d$1?5jA;QWW$g$AaR*9gk^v8 z0t3OTS7>QJXmym;LR))a^{jv^jY%3H@Bv>xmc|kagb=C#iye7jA^nO(!NaG3ww7#x zLAL!7>AM0`K|7>reLJN2kZYdpd_W+{Dv64KcQ6&7AWF_GX;-eg_aG(LxC+!vEVF<2 zZeX9jgXEyT12bVn_F!wSa|(|+<<5WdCoZIzFgv~)Jzh9>@F`;gfkTkO^i|>gNy4iK zu39ZneFD?+&zfJy96rD2W}SchH`5h7&*?FvWiu^~iNY z6~b~&#q!<^M-<{4VMHTJBK~3bI96sc9X+FFX3ivRb?B!zk!ZvMUI=t#ARueX|Fj3? zw_frOFGJJPKxZ)iMJhdw-P5zlbhVz>zzbzvP72!i8`{9(^P6@mbCIw7Q7qNwiqo= zS;eWDLXlx3PeCb{g|dHSY|8mUonc4PE1R_akINt$UK~RY1%_M5B)SBSjt4q4@FBAkUn>wfho{MsP}H{0f1ARP z;oz_5Mc%}!!&nA0c3@sgf+UlXU2+tX}N zY&Jc_YKb28$!vcLdT=+q_`+tvN}OR%3Rrrw$zoz6ec8l#4A_M`h>_WNTQ=}HDtvN- zVN*Mhv0}NhLVvU6Q(d*;;1a9Lm!&F=bqdJJQ&>IZ$_u&RzD34{%?;H~veaC)_-}!V zirTA1ypmW2bEPdswdS0;L#nyi+Y=CdnMUBrF&+81VRw`|w zLd)^i)k27htInQ9lQ_9B;lDqWrHun8$^&E#d4s4aYsrrW1T-gisN!W1xE#CyeVUd z>TF7V=}2Vz?i9Ts(M44_ehu4=ZQ= zTmz$^ltN+lAQXBSSQ4q0q(NYUlZpsftt1Y9eCs2RcXRTE)fJJzHsnYC9dP zM~JG!rP+57xN2}@QSMgm|8%#^C}a;h44;C1E@D2@^)6WRw~2jiqzRG}VB4OAWq)=% z$Y#Fn-03SB6-@_YT@~xNhyq~R!${46(MEp)`pqKIfNUAhL6r3JySUDs!L&Q85`|E= zG$XGU(o8OUnZq;6l=_>i+N)&orh)}=Xg{efxZ0csPIm-AfYDJXXKS9>#%tjgE9!F> zDW{b|XlqTld#`1tF@w)b1H=N9^kzBR&EV~)R5Q(CL!A^f<53yq!>{NGGJG<=$j*OW zXuwzzhm7pKlPp(qgx!eHhTZVTf!nAQ+wMIVDGi{Vf*Rs(i6)~3G)7o{_G*z=;6Xiu zPv~uAERSrjapNk82 zhM@}GVC=k7!O<03sjA#?-sz(~0Gxj%wGg%!4#!C!FbUIrq=v*Jjod4OAM7h<67C{Y zr^>g(c1+fhb#Gi+tk)W}X$et{VpeZ0Dvxr=d8adaYAs5G9*yM~t>pRqxe+o%TXhKX zyiKi|d0*SHlaQT4e1k$A(=pL|Juc(mgRpJyIbegT8S5(^Db@FlcZ2nW&EtP=ni{P= zg%QyhOj9np6D%Ipf%Bo7=x{vbNmiE%lUaZbQaAEw=UV3aO=}mBSWQluoz+-O;V$wA zlT;;)NN_JyCT0?<2)K?QKL(R#9eRI4uYEn?F8T)x$D9E60|>xI&R{l(l`y`-&S4sU z4*qBu7Y;Dow>%x|k)W@L4{3k4c(bvXqSw1dg9ao0RD(mU##v-uXg~PWz${4->neFM z&7f90gWQbDJ4`K&GxT_jJ-x*oDD-e)hbIiI%dgDI@QS^~ zhEg;!Vtu@Ww6hXKm=OxI)iDOahXX>igS2=&b?5+%%O{4;d*oZ;cgFZ zO$JNLep#>(!SYvPIm~}NGa9(KsF` z;Vfgl4lnJE@WuQD&t+uSVCW+j6t=e1tB*X|y4_Y$&TPtZO7LIi zE#-Xmn=-Z8SPJ^hR(Z8$_G1`q#OLAg)G{aj&lZJ@*pm1L0itOv#vya`&ANi)mbP_A z=qs%qX;?hxbrx>RfVj012Hr1=A3@N}nE`Si$cY*Rh&_M4Xj<{vs$szUf!8zeI%CIq zTEIh6W`8jLfM?6UA6E8gmFLpx zBps`v`tyG<%2HK5gRQud)?)UxAP=}U;}=D40t+nZPO`odEK5daZ<7Z06`31d5_Xb< zXsN{FmsP*>IO*qZ->i@vId^{@SApHfo_1{bfVP=OJoub#_$Eg3 z+J`7L`OL+oxjPO)(^2s%U8}MLLS=Y(+vZ%2(b&Ja?9EMECjgThgia z%6@1opVdxOPb$$-`$2FxNA;rQine^3M&u%1n2oHt=x&pJ#{II(M^j9fP+i^7g)n~~ zIwy^EvJ^jQz2RnUW=bztvtlP27Ewa3*n7_-V}L9pigH>DPwl|Beg|m;+O!99n~SAx zL_BYRVr;{VKZMQ`4RgJ`5~06G)FD0gPz#q+06siRMQohC1#le8wk;@TW@d|-(PGJB zvY5$2Tg*@bi)}G8Gcz+YGcz;e=-%_^zyI#U#7w-3*cn|>xmHzYc4y?;wKF&K{yMj` z#nsfHreE}lQ2#TzW+hE%3546b4va1xOn;;GV&QtH?{*k)!FLWmz7lnX6;}p&?`23R z-9a>8p)nWo>XDubLO?Yzi&6aY;Ez=R{RVUYdUA%K0h93=mYsu#oU=mWkzr^(TJzqJAJE zREx6Xll_!+l(|t+{~e;^?1{3``<P4MJSUB7JrfX8dhY6ohaa3 zh+}t=|D92pZCDsltVsDU#_Y2!DTkN|)=wHh!0+hMz0F+b#I%+Qlj${`;^SxID3!-; zAy(nZVMqfyFKMlF?(V8EzYia3I8ssibT*0ZGhN(b z;Y3?erN#Kr@HTw)?7gbTtS8Er#$BMuUwS*L{pi2!CA>dGG^bVsz3|21d9DYE^!R3h zCw5UIJKn*7F=bH`p0~ew)T7yTBVPw8Lj($qofNWB>6|_Ma&{aV;Y!yRgQxALEBP>b zTG_EIM9(}*W(m4PZ#H8TLNvlpm9o<;?exlmp(taMeMu=KrNsR$I443<6E?)uHjhnun%~S5_>9L7gyQQ zf2SyWj1x)BVu67Zw2@nts;g~ECUeer&)Kcg1wFippiQ0e8WQ*TPiG~_-k#dz@daY! zqp_4Pt<|uj?nsvxbr9j^S&LO$*}BE*Z8TC@5_uJ4^9>f_l_|*s`KGC>%T2ogGqGSi zvDfYOcTU~Fp4-khii2fM6OA{vhlW594Ocw;M$W|z?uS9vvtG9xGZq>6yK&uUh zgcVvp^@UvCSePz3^u?lB|0}HZ=f>s8mv!}7Cpb~Nbt%{oqRgEkCSU~xgl*cqE#zFe z)h~_-7wB~S#7rmOF^@TM-g0J@e_qkqGIL^%g*tL!Cs|f?`dkeOC;gtPO~A8?j+5jP zihT&RHBWF2JN5GKrenGQ2VzyrU+F)?0IRgPk~8d5C;0mnY(KR9g0O?&Yp+VTDC6{; zWY5$?*h^vsw--VvaotGQjUy*WyW6j9)(^M2cLzE_dWiVEL|nY%sPDNUw<{;@^qnS)U3Xr$1n*wVSsRbDl!W zB0LF$`M)^HeUIXpm*?q}1!-2Z3?B;KF;`wE#LLc&D?+IFu9;08ENLO1tx~BmR#UHI z3LOwCD#vU)dw9IIweC$YJTx{cD{#$16{Tlsh#{D~=$d#947n}xW*d9so$mr&$DrQ% z@Q!kM&!M;Gm0~zVQmU}H+;Iv*3mAqF`)J1Vp%K&BXeo;#qLOlPW!?-Blp{jcEJE*l z$-(8PN&)H>HmjB7fD3B%QV<==nzr>M+)xv;bA{nUUYs!Uj<100rMYdWw;@w?0EIX) z?l=82-lQx5UK=GxhN1q4!u%!F_?EVvjO0=|C;PpLBvy_J3=4;Y%C)(Yrj{h*8@8g- zxmfFLQ;PSY3R`}RtTuwy*80>8+BQvoXbz9!V|t0m2w8Ab>?w=2V&Ys#dM~FcR9;(8 z({F1FO|}72>4-JDP}kH7noxvrImqv{7f*lGHTdCxx_biqu47>RPlYrEw1MPbF3dj5 z^iwkZ1kKooT)9lYHRqxDnRz($f+K{jlJU(2W?P)?+{bCnuCKC6!V;#6a4ib@n3B@O zUEW!(@u-gJGf5Wa<}wo1$_xC}8DvOUsj~jRP8TzL(9myxDI}|8Y*^d2bQOGTZzM?{ zwO8K)ZhC)J-Lr<;7`Ylktw@(r>~n}e(jU9z^s-i5&0)z`xMiZjerliLA-C#J7()sG zVom{a&F1h=*X5UU=JhPi@&}?D5W6Y|uYFWMFkf6%?LsT5XB{py>u~!531R!iG)pnS zoK3@Hc@HJ>iePLiBFV=Pw(s&=R(@!UKj&Tp(rN-FniegNjtM_Z+ z0A}#3!8CoMkZEhN2-g1Owg$ttXCl@J#13f0Iw{-f>;gkH3n%{9Xe8ekL=QSF41Dha zK@>j*J_)wGFc|wIxJB(O{T1Dojk9FzUmiH1PMh1oQ%C4b#3O8E9n*DoDj7@B$2H$N zUOCvfd-pk@kFjadcEq0_wvPot27i7R0EQ(U>$*ylyh0Y+S&DV*<%)17*K>+M-X@6c zSX`^ZaElQPBQ(;p=2V^bagFdRy>c4P@aIfP&cD>=tME4R3%H*NsL2y4b)+h;A#r2v zNEs6vK6xGz3A1^ZgLzueY`7%|ctGm6#b&NWtmth@C#3f7=9na*a@@Y>uoP5l0XfHM z-Gw5tyWivU$nQjd;(q#5Xx@oDJ&k3{o*M0?=x$#d`Z4>mmCK+!BPvJlCfgJ;1Pw>X zVF-t`K8NSM(1)A1MY7H&vAr=v7y7Vp^@B63YXR?=gc`EaY0*_ zCnh&AnyQ;^mO~bi+k^3;pBsXY0jc=sz>jJkI43aW)VJ);$(TR3O$qWP1rOdI7Ws*) zEjc@oSF`?t(zddT8L;X4dp+^JP$XH9jxS65Q{mryl>DY}_b_}kOG(EMgY6HDIs9ow zn|0_`!{Rl$V_us2$!h3=t^Y!Tz7B;~B7*qB!H*Mal+ls1s9Q-#-44S00SVYM{$+RM zs@e}uPq!f{YZT=lbiigLW|RAzLxOHs_w20`)+tvPc5EC`1s`5F(n%*~0>!=j zpG!7BLr~jNRN5>oy@)CgO2|+cJ;CV~SdV@jdlKnZ;r=H49+uOQMA2}v*j(stt)?Qg zVhCE+xP%cRy#qM-xFuh4pd*R+d5{WjHK#Rcmk<^&iLZo2_15Z8f!az^FteAoJ}`f} z{fXS0rdy0-8s-Ka$Jv(*%ihYKux^0CZ5sBD1#ipx%;o~d>EOY4kk##+;ek>2WrL$&AHkg2Em6T&+C^;XZ7^r?RhGdv-&Zz%!FLP9R0F8xE38 zAguYhogd5}^ZqzpkAQREqgd%@Ww1uQg>NPh7!$K0Uyok>%8MRG&rzAS88!^ZrlYgF zNa(nid`rsf160LielIhIgx(-KrfFxtM)$HN9NiDfR8%yuMy!acu{yA&x{W^zvdoca zqWw8zP1pFrzMFQ$23#0M{eX(M_ODLsvV$+D_zLmbIqLk))1Uc+o#d03td z%_dbIGxqFCBgVTr4prvVyBd%aR?!fHiyd#{WIyuob%a;<7=R3aVO;{Rh=_YFf*MQaTQ_TK#V`pfd$VJx z*7hsWmXl4Uja!_ua9%;-vCb|u^qn^y$o2Bu zb>E%n5_J9#i{a3&HL^uo--K(N?sFvrlLZztN~%0R?V+XD_15)vg_5|ZSOO@!B) zP0iZs@>f3)+KV4!0j@qrT#(G7wFWAE*VB>)+05{;91>%h>pw)f@?6di`VI#9zNpW) zM8Op08AQYHdrK|YFGD3$EV$iRoaoK8O5#%`ysmf)6?uiw-(l<(q4oMoOu8~G^+Edt zD@AK#?E>+&dRn;3&}Zc%(^$u6ZUghtYvnL7<|b#bRvu1W`TbM*vt=cl)e&9#lr$94 zq6oNF&Zk`=77nBScoG)BkJ^kZbEdhAD5x^?*3vR>M9WpXPQ+I7psv0lRtQQ|*fkw- zG*Y0|Q6AzEDf2+y{JziUL=TkOPqGEK=Ajkjb_Vc-KZG2nr)GZE8gZz6(T!Xdvp^G2 zaDB*BgMetqwO@<;y_`5&vVVorxEP_Y2J%K4k}`~zwh%Ka@B_471QEPJ$c`zGqMp_ zp*r5U*F82`Zw+KAFMfT~rd41!GjiX<)DyWD4=LPgD!3D3rG{Lp-G9TJTvz#oQCq4< zj$n*rr?H6RIlo1&<_;$|5tX@^w} zwQk`kFGH<5OIkdoT7HYEZS;7GPvF^mZqKzJK`Y}HjfOeQgd@bPdjk+ZyEu4}=M#x* z;af%pB@sSim#|s{+rn5th$Ukm+9%#az!KDb=oNwe_SmD@vp?*V>Ji81iMyJY-?IQo z;is+Kdp=O692j*a-Ysv}CGYjt+$EBu5j9<%OAnou&P&BW5H%;bg*y)n0D@A5{^-|21t@@e9uCQ8hMZ&gRZ5#n=E0&Bk z3Kmiha*p^iA*-db1ZKu${pK`o>~Xo?c3K@sOW$AcI@>6tMCD!*WpH>%Np3Fs>AU@4)9gitwY;-ojVO9in|pZAtSoIFEHXpoGYWV zu9I&q^h1<^@|5U6rE6^06MQuH2+ySFf+=hMiG&Gt1Mh83AE4c{UG#@NCAYB1VLkH{ z*3jH`4+?{dKhfwGZPV`?hE1WJMA~ASc9}|$OYPV0o96*#lB1>Uoh@L1SZI1itSC#m zqbVw3t!-!@PGGF-rGe863KVSnb%E0>MgDvu*-@uh9|V)RP1*3K$l|Cc`w*%GCA3=z zMuLDh5Us5{#D@3XlOGmtsdX|w)}3VZC0)_PUZ$6|3v^6&Kjmm)XmlQ_Bb;%Q$lxRb z0%l5VzR)eXD~4p=ya~vL@HXZ*nNg&)Qm_M)+v!1Tvf6*tW=;8qs;3h!C7mE2rPKW# zkKP_UE~)NaAe4vscCvpn z&paixdmYRY+6Q>Uh63F=?fN0z+2^>PEM1ipd6!0TDpRKC{G@=AsZN1n^TvpM$yFz+ zq!6EyU9b>8jL?H^Bx;0`9H>)JnWgXAkMrt?ZDv~-{2!ZocNXsZinQnL!G+;%J)gZKwq%u3~aK=rwzeqOjXp} zxLEWYKzlSlJVG7hu)#^CP^d#M05tYPM^;TyURn0LW&j|sb~+K<?dg&gxygT~xJ zH}&=+e{BY-r8&SjF2KkLLjp^6h5^0r?ug{^ccN-k|rOyY7(BSy^la2k+eRW`b z{qT5T_;?4!?Yf^f*v$9VkE5+fp^tdxGi+{_Lw%?ZV{`aE?2Ok8Nr#WYQqGq@w|jV)7PHUm5~F&Vd6gYRSFX<}+*b!v7!q&T%7mB^RDCb*gn8D%(C;zLT z0@bsqtCEYc6*iTOx(bVom5Q+OP6k4CV13~JM>xqjePaKPnL4OXTU{UjupkA>$4gOh zOi|A7w7*4v(mvDHyb+5Q;MP^W=e-(7TnKyv!_zL>=swRvdg z!M_5b4-qbg@G8b7f`RKUkX1~NJkQf_y(#At z9v@+l5(k1cuPpm3p6wmXp#2{&r|mXV8$SkK1JaGII!5E3u%3V3cj-@FsR07mTiUY? zAuiro={z5K?EC;HuF5--V*efx<#I4#fa=XPK-KaZ2K`olxkAt1QEt(>_YoO$zyQ3i zjM8>&?yY?=yfXPepNQG8k-d_ET|Wu#O1i>-s4ahBdxl?&^@<9~GTg1#Qy4S)Qi2k5@M!pHI7cA#N-%iO9V-(gR=_I^ka-Z6A(-wo+Q=uUm4 zVeM(>H-tKNBr2l6e@qv6j6A7*KLxN445k4ZmizeQGU^eg;*vif^4WU~Gum%q`mza2 zA9;BF+QkjD;2nj(Zr)kv0L$D4?FJyq2lLG|`g7Ib`Z?-*ADGTHu*~urzj-Dxst+MJ z{GI^uQPjWw%kM?pN$m-{@ZDIqZ4KoPUO#icW1Z+O{`8C}THhr7?jz|m0$|xFCwdHM zV!l*gKx^&ud_md9-XHG;`d@2!j(;BBW3xVv0_cNYBR8C>k6~s`_Z={d?Rjqa+t1=E zJRij1dcgj8)H~9t={r+@Ge&OL##j^Ijy>n zms5_%=h@JwQG*bGm?ez3fl?8ueuo7Ce7 z9-S!}rr!kH-i&_D{(dU&J7i=Bg$?pwH|Q_RQ@+ZpGd_a(%=d={@|jW>1jNkV(8Su* zCF&iEd03T-rXEfE`df9{XipT8OQ za*P-#6QITuQw|e*NrK3aY89u}yB08LcE2gzD;)?*zr9!8DW zOe$nlIO^tu^cVP#u;?@d_NW1?C*cOnzwa|Qj{bW1f4yH9F8P4{di$IvfPzD>Be5#Z z@&rW4OT*<}Qd^Rf(jtwD>_mqCrZb!F^MbsIoq!ze(ME-CBn%d7jSUS~$?_o5y4q8w z;-j!@VjFX#;%|caERMWW}a0 z4AC5g2JR3SvI__bHjQXW5ii!o3ez{8=xpiFu&o`uLCmZu{U>C2$%;Cy-mB_alz9(j zCG!bgeXe4KK{19U4)YvdzeRfEig5rb5r=lD7{rnpuZ3oQd_WktY_^o`W#{^QtmF&0 zC5`3WP??PB`EK;akZCi7_eh=sMU7-a0<+8d^W&Z_v>toc*USI}(`$%dO&Qw(Y&UZ8 zng;O!r|6ycNUv+D87(j@2rZ+}L8flX-_3}EzXx5IfI;WhL;6nzQhvu1C)ESG&rFNa z{5%ChRFtTKPT2lr7GAK^y9nqHWfr~6E0?Lx-@3NqA~U;6;g||wBO}^HPj|`Te(N@Q znHh4b=z!;2&Kad!zURQ~|DBE6am(Ed%jF;jJjnhtb9tG`KCdJo~;7eVb{3Y}*(=f!P>`mgXs2 zm((K2O6eVZA2iEKiSkG%>P@qq`ORDeM^~%$(mLb$Yi(fkDi$h^9RCVPTD3mv`=L^J z+sg?vSjtC$6ey{PlN&3o4n-c~q6y_HO0n*a3(p0+!%^)>YN?6cPSR@e6hMvJv{#k8 zrmQxg1zBTZOSi+U5h|3TcilUO84@=TN{h@;6roDy+IVr2BvDmIh9geG$tU?P88*vQ zML^K*D0NazKp+7xT0WoD8=&8o`kS2_tm=6QRIbi58K#XX%P|vT|_1AEi$*k>Wy^%-G zi4^>vX1($V%B@bn3v0vh19g|=W=LZyY&RC(`O$nEcJ!C0`KNnu30E!H#Kot5wF888 z$teF4eaPhF$kY+e;3uLl0p&#R8iu3f6K#Yijn7N$+C@?D0x)UsR>77;x}%F@2GI_^ zdNYhWx0}L2-;Mc`nW>XwH&~lTm~v7@@6elk)MStvGB>t~<#3PxYnPa;yJYMygfx{i zX2TTRqd?)1!-Vdbb0|2V_HR8DooUz2Q~~N&2({>P>E4)t^@%!!R@*!`OeWNIS!#EBf>x1|^q;H)G4iZ!!L%Ax8OS`Q;|X zvZa*~jM8R)QUVe4Z(R3b+tEpA0-pZ}DHc6Wm7U9?P4(HtN{-Lp{&UFoa`H-D z*G=yAKnvB2OP;<7&Xn$sm-_nQzNA#8BLj^=_s&N5DnBQEpq;)Si8%MqL3~#XcKUlA z*=4R?7CmXD4rmSfYEbde5xTm{IJnW@F~U3sn{6f16SF_nA@j+VR5RhDL~$IjK%l<` z=0>x~0B+NjCm>0My}}t+`-BuK+!niRwzt$XP?K+`i28D*ADq;0DE>Z29L4oFgEq9{ zgIs*ZRki98*zc$@z^kCk_kJgqOX^Pv9n<%Ym795NGJ0haTy3;?RW8lW(4(AijvL8= z&I1|N3f+h&su$xxQ-W8b_cwt8lw)A4!cAaOV{yv=3?KS|I}lgL<6#n&tL5SZry`>0H&- zkmLhgBx}GK?znfdlP$6l*!TM(BvTU)x9~0(bT%J#97!c>9xpebQv?USEDQ zie@a&C+reIPZM-oFlwR0jgC)KMBarY*H@X1y@!L|t=~t0s+74Cl7f|qRbIT?U}S8$ z={k^~;&FFuFd(Z?SgD|M?pgz@&;nx9+sYVj?aZ-$<>xoTSH)sgfU4xXqZxPOAIX_V z3{i7x!Qej@h@o$q_5l?1&O-2FNt2L96NmFx^~wkxxE48!w7(iP(tP4&YVtzeWM~#7 zuEZ_yzMr!=tf2_87J;D288UylBuz%jr&=GMF>q${PV7=)PZG;nB1!Ld1xwgb0+{jt zd`4{hncu>C6aRUp0y-nVIrgQ)o9+lzu11ee3*@Nu;|axAgXP1=Uw%6Z$^S6CeOd{y)oXi@J5i(Oo7Q(Zqw|rFR>k>DL@rV;mMo=zu1(k2fC7&i zW)495IVs#(wh7RootlDQ<0-?+Rdg+UsSuE`fTPi#W@WQ|afPlfEJmbD4fw@LvB<$b zGd-5La>$Y#abczceddmK7i3o_@f~um*|2xel3JBx%i?H2eX7TnQ&PBKiM)XL=AgmG z9_(JO!z(5RZyhrqvV9T}af!`gmRy5TLyOeTvy|ewYYe!>+AEJ8J-ddi0$H@t_2H&T!Hz%a4^_`23(+%uM$|C2);H;$(G?>25@_zHb5!Q? zHS2m5dTo(znXt{gwBe>TDU+nF*Uu&BYd;-=*mS$R*jr#z>Mz(NI2`h=Er>l&!&=pT z81=+q@Sp?v#TYS-RTqd$-StCN;Oj%$)rz<)TqCOqll`i8(oHA6=MO_=MPXMd8*?5c!_KEZnS0U31Ms z_A~V#@n_=vS39N$cPFZYLs~2g)m=u0ujoNwmstbE&^0{kF!?*A^C>c{FfO9P z2seOb=h&6Jv9zg;E{(&G8O8b?Dg!tfMayvr2yADU2CZVaxQ8KE8d9?1}9&Oa@fsw>8^(=jD+i0 zo-3JTK46iXQA-&t;`2`%^86MS64a9UftR6uJQaWG=_SmhpL5%>4yNaqO;~W&7ZfL< zx|V+f)BJ9O9EGZ;_}XZ^r@9Fyx(QX4_)cMwGO>{3wJ$ZMOtWWmM{2nK_3b~$?ayPb zF;Ds5AO2VIIiiC&*c&tbR}G=^|D%TH=VjRcRpqCl#jpRPUh0qh|Hg#$e^#?JbT<4y z>i##*&wA(}APFeHKrsma>$YX`1nQqx{`Y|fLXZ%p1&Yq|@6LdnIyL#1{YPK?zr#uX zfs_6Z;S~SCzx)s3?4R&10F>i@2p9MV{`G$d7ykz?LH_^Mbf3PFgi0?^OwfNntz1H< zHz*7#8dx_I@us%#$$r}{%c^@;dd*$Hznd;sT^qM==f5H$6Y2nx;??V|m=bUJG0iec{ySoO-xVE>C#~5P3~Kk^`%1yt#?{1f zj{5mELYOh8p;5b|pnN$RZ|@T%oY~kMIVY znf!>svz8%>S*a!3Shgm%z5!W|5TdfSc{Q4L`YhtC2i-fQv$N4XVHUkt+2xQ;scv{m zk3w&DT&r+(vG|an$f_g+-Bpo;SDcJTXf7T`U&v^B%hOOTg&;P5uTJx7S4IQni8EXa zl;vDp=`aacjeeUpoN)M}mX3`II_48q2qe4dO2|qO$bZ+0;*-NE%7(gV?KQtKG{`g! zYg7EuyOG<>F)x4831OU#{^%nVXZU=lo=#;mBrEw`PIA(n5!01Q3fU5+jU>7na#_M3w~qbubMoc4puuyM z08S+L--UJ&8OmHbM~R&isgIrkTu%bTW&vx@4N=6E3upmyB*asMaUtnWQk1ob4R4(CaYiJJs^<5b1F&3^DR87P#Dg6(mB$jV z^GAmq@po%ax-i*84?UlBPRP3#$Jv+6$+J4>V6HFwi>ryk$hTKFW)m4@NjLb~2C*<$ z_c{<+Xx@!jjhtsq_SOMa{ZWj9RLWJ^ZvHBkrXL#BePcZsP9$vFnH=l&Ag`4y&m*EzJSS3M>?A=jz8n{x>0?9wvQO|RTY z9p8RSav%xc%F?f~hX;LDr%DI*3uy%YSEB4HjCRX$Vv#vsp*-00qhDti8QIzzic3st zVNN83EjH~V?CfZOH6e^bRlr()5U#dk(>8CTV5lC{2gj+pgDuuM*YfTk<-6u>1#+}_4o#8lWV*T+?%e6!J`g3AsDj|KP$o)DXi_$u3s}zAa%;C;olWclWoL_6^B?sFCt^KZ44q?#yt& zJ-nTJ(yjpo8eaPi=?vi3Iq_Ty3T)0GwzPg#mUdDc-B9pc>{5cbog6U3Dv}uq9l0@L z;m;7g=M9@hjot9o-kgqUw>;&$c?bV`VCD_Fl*gBQ*}Xj_A>86fp}N=HahmTaBEBP$ zMHU)eCT^E|>9|EHG|Xrn?}EuFhNNezr$@mU=rPF$ppC-vNL7|)Qw$t$rblI+@bUdQ1a+U52Pr)lqd0VBwbCqNB9JHL9qmf3(=- z-g4gnccmFaH0oY5L-kyv^R$U4D192wlSU0OjIIeWs8f?@ zeQ>}I_0E>bY5RcXt3&OcFo{n3j3phU>Y&J+`Ja7IB;_>}6O2yC$$sfI(t}F#=op~@ zSg_pt07R8>r$;*zpS!_iICmXX#$rpb*ri6md}I3?6pg#ZRAvCC#iTRX_;(TPhuPw9 zR|k;Vur_8h))sAg>8XpgA6M8|wU-wx;~^a7s?T{c_qaXq-c5T3?9Rq3btxyz8;|a< zDyg*SU=TkHmsX=2h|J*;nda#D5tz(l?_A-5Hl;Xcr`!l?_W}t}y581K6~Ds!s5Vl*2u5!^8s4kadRrwf7EVKZ?yWJ z%ML=|_$vo`NY%wJ@*_xljV1Ia^vA{T>4i$|ZSr7~vZ$duMk<6YlljzbU?#v)Iy-4+nsgnknGhxW(! zr0;J+>!%>G2@G@ZjLQ>8%5$j{-?ItLy2Z1S^WGSmr$Zwm|Ck;AitGMu>wRNKXMNZ@QUulm?kS^~5k1YCA?Czw)k)daTqVSNT;W8t``9SUOD}gDVx-cW8kK zx-lVrU33bM1=W)GofX>2H8yP2Oj6PI>a&2UYTh%AFuSaAq5N@AX-8pEvDd?X*y{$L zuO1XGvBvui-R;r4I}kHwU1qT|9N{OW-4yNE(=P%ZEe2B@Wa+r8F&?>!;N%u#OS+b9 zo(fZPaYvRPl0c3{qUEj_T{8EcTxgs~vP^~6S}{r*W{$S^8{A2>v*}lDskAbyFA9z~ zzS~R|1w65K)?V9%3*y4l6@M%C>d~#?57$CW`er9GLYH1GleCirG3)N;rmoNxM0rD5j1i!@2&o(kI%pl}tZ!H2+c%)i zTcls8njrqVT7cO-p00zKs+!sE&3b$SH_N3UA>c(}xhGNU67<3#8L-bOrjIfT-UJ3k z+fGIGftsvXyEgmzIm$`{Ko%#jrDCRA_**zM=4M;nE1BQXx&(#k#m=#UPIZswTU z&+_Pqju!9V6#%WgVftsrz+9bGrY zln=hfp@RHF>|;*k!|hHYaS}=MB5a>`$_rLPYvwR5E$WJyY>Xv5*V3dp$&^FYP@HvJ zpBmqgK0lG6x~776CaS0TWbRWXSl%ZMXbpY$5L#04;wofXq+_BDE+mkMQC$yI)Q>vB z=ERY(_A+URO^4@FW|-+=CZwJLFNf$Pa12nhmu=X5HsvAw(7@w46)|A8(G5q(ey8G! zZpIizE-#07z>?sC>YwsBVG=2JouPj(d|TUifTg>TxoFA?aiqqHbCl#Q^mK72!@qn3 zK>y_-_$LIw=hMOPdvr0i?o%+1v4DUm{G&IS5_J4QN&nHUm7kjRU-lomwINI#v;px` zxc)=r|4H<=C8rY(1p=~w0s`{wA0p<2G!;+eKu+W+Qo;rd_t`(H$G|A_DnxObCd*#8I7f3+!O8?R=*%J;4(WDpQj zhEMxPgvU(02*)tL@b6Z(%1T@bvMEdAh=|j)HGhn2NCBiluP&N!@OGXrz^k6^C56ieqe+5i?p41*F@U zGdI-9P9PuVQfpxhkCG!hu*G$w6%8hZY2J2Lz~Kif9Z#4-UwA?q#u*}$Ii&+x58pDS z14R4hmgi+PVZc<|5W2mjvE-uno)9#p!6k338AOep_3Q|~NHDa@nKLLSJ(4NLt<@zs zY{b%jFJAkmU$Kjur6Ci4G@aC_60Gyra^(evKiDc*ECj59VRh7Q-%fULvWJ=)CxoPn zSNaQXjX@do6iQ8PC;ScuCc`v<*wO|gj5)$HK!Wu|kOV8KXKtieq}wIQ{^-Py+sOEr za#trJXgpIvw1BJ84w_L;a<;9hz|MuW?PI-f<7o4TH2T5u*lgapiI95C`4|Qr zZK3Zt-g{{KGzy161I=vHd9djS(dK1*Of^Udf>xe%x~|7aUh;&?D0p6>LW!L*0} zz) z)OAU6Y5l~pW^Xdm9d29v^XAx|OoCSzdaQCw zKWY09kQ=nL|rtnX|pZ7NWBZ1t)2Hg zR#gwAjQ}vNe=7$AaRcB`T|!81In+g(ur%E zIgD)uN)pFzaA3d8cO2o)C?xmT0_u&|J4GvJ?Bwl<02$$lW4G3}%%efpcHbkZ#!{0M01?TzGykr_M!HwO^|uW?;wnoJYM zj#MHDF1zK0FcDS4v36^pPE_qt7EFc4%_#y4910i&1Ew1;jxWHuFF;+JLZ0xsV!SYMLRrn4}IXQcMbw z^)u6sL4JW&02E!PGF;6a!XgvumapXKX}KR>~Z$(}A2UP)O9f6dJ@ za67L-*3dy$dBxYdpc~0MkLAI|1nFH++xg?Q5KoYGr=?{NOR){#U24~3H$Uk>@)YKm z@L$F@A?mrQuLSV)(C-H(C{tLgG;v06ZLEKB@URR#W-MUf_9Pw<`UB9L_T^!)c9ek; zQf-ZwgzWs9pN=Rn6D28C!C_w;5MfQ&St2`jp^AklIucob9zpGM)VZS`E2O5Ylk%75 zHL3;0OU3rpC6#ds$#KF@x{Yf793BX}yW8s(r@Y0Oi-LS3rZVh?rX%8Z#t7q+RLmim zq7Ox}Ce5jGaq$n+d%zo=p@^vn4vzrtxCmA8b%*(Ch2YI3wpAf^5~cMkx!<>-Zy@iz z=q4|^LPN{)CgYPD!)~VC%;E9;FaqWRYolbRV^`!6Xf*9zQG_X<2DdhFgDCk+AAI$x zsMb@ksw9$et(p%Y^P-KIGJM7NWblr>^!LLZj)nIv*`Srp1lbx&mQ=OUW+HSh#-_dn zd9P+mt^aOf9mM6#Y!#bm|p&S=D3$#U#U!pcSGUx@4IPXzG^7%<#g=t4? zd|?+=YoiEqUVo8y*S?vwq4hA1)oe&tV=1VzFDRTO_r2CN;6Z>=f&v2z&6SuDYHcG` zQ+XXnySLX9QU#9{Z;ANI&x6p4MW)RMciC;{TTb6gC!EG z?5;6jUgl|`tKZco8qGzJaLcC3A_pbgl_6%}Baa}F>?*h{pF*px*w_GEQg*b)ulnZF zZ{VUC6!3De4a=&i2LN(HqX<(8Yu@}`;yJ|TB$*)DS&Pp3RAScGT*{$QjVgqY;hGXB z8iQf2{GoZM*`u)U8VvM*(VsSZR+L-`7_fw>%FL(@Y3?=jsMpv*mn|`U9et)ZT=!z# zqWR9`^ZP65@ZGZfD?xCXhY0jbFi|;d9^tNT(*A1;B zwl2$$8+N&`^|(7<8ksvf8aUk5b&)!Rn;P&)SFkzFlLZj7dw*ySFQ48EM@I^dD>IT= zDiG#Kge-{$58Z}>(Uh%eEm>gIie6nlb7bM-$u>W-IWqoVoqYvVRa><7p}VC+KtiPi zq)WOarCYi|8U;3uAQBRXPU-IMMp9Zzx=T{>KgiXq_ul`FH{M||*4Sr^`OUfJUTdAb z*Ph?EX|(yZyVmD=JYNRy*S{?qF=c2)&9;o!C9(?Ce41UjWkQer7L=F0=_Zui|MuQ{ z+l1YWSwkdms(99>9aW=;i=b)zpM6(J#cQUIGq|#I)DhC7Zjcn&#Q1-@Hg$X z*?#Yl${iLc#*)H}TB8;}H4AtS6vwF2Rg4ykvpcy0#G<4GEBN36`d{=)PO*x~7zalLU=h z*6WzN>o+IV0%YC;-wV zoIPqN2v%TM5uizkYB$)5?mea=>W5^hO6$RUE8Wh3-9`y z*sT?ARJ7cp9w5Urei~35*V!&Fe{;TalQSE8eE5l8#M;NJfYSnCD~pQnCGe0+vo!P2 zQ@;F|$0B&;q%dz_Mnw&WiTPN%--p+dt-PQm`!G}MeQldjj@C3ea?|H) zgCXi?{B3qG5(Qn;gLk7VuDv%6>Mr3MA;jU^+8ufbIrPZBP!5iFf#_lprAJ`T~E}Q~(dupGa!nmA)4^W_(iGJSq8U*mDr&pDQ8e>7Kz@C6W z27f(q4fTX#1rvHgp#mKmRyNZTAFlbEF9ZBN^)k5pJYtv8yJzUdqF2$;~oO=Y#^wAM8LLRluL)R1-n%~nK5q_l3Xwsteeoa}1<%6SuE%yH{|Rn;dg zwlxOF*KX&s+Dukyg$|2~!on_-6$fV;P3BbpgwB-=6MVr$Z2y)%@+(43x7zC(0P%~(o~J_nNuO~0co zFFreStof>RI#I-v^kIFml3}D5BhM+Tc6V=iLba$6-R!P^6zU_USY8 z0c|49&x{xH46FAZ9QDl-tH`JbIC4?po0)2lPs3ppm1f8S*lesV{(2biwrC+wW2inY z;H%J@@9>s+99MJ1W)H{o92XTLT#+T62F$kfy8>qli7$6;pDO!YD+9=_ zBUr3x%jXRM5?1}@`#Ny-PQo$5)y5h-PTjg(fXIdZQx#IjSwgvs*?2*d{6d?~iF8Q} z{RE$*Vu5-BpBH<%90_7ygH3Wz>8wYWOIZmpc`_$zyl4zQC_9QzBzIkff?qgt5ha}N z@8=^R@tF7|75jM43T6RgDr<>(16^c;L=XioNnuey@pz+DJT%Y-0jM3hHkEknDBiX~ zP-SJxo1`)#a7^1!xfy(c!!pp};y~T|A&}Xk-S>&}B#xBf^{jF$W^GgK(c`qu`UyhM zIbRy^W;BjVYG~ul?#15tYmY1&Eb2?E@?!o^R&fOkC&uO7YvInU;5KD@j4n2Qme87m z9N9GBdC~afxd0YY9VI}Ce>~8s5I#*6H+jQ+47RTdx>i_+y(yB)-RH0mmH)t56*5Ca zJSDQmK2p}+F3|9LwK-GlV)z`HqbQk=Es(IFiBry@%`-goeG%PR+SOYFGp@yQmYVs? zqw^ZXCFZ@68nNn;VOrLgIAs1i;Py_yOAze zvr((xW-qez(nxv*LqT_^?@|~Ne(ZchOpLthCthq7w4!;$O(9KwUD!XvxJ^Rg4AbNV zcp$bUiT39Xc|_2hgk-b1zyM75hR>t|hLu1Xa*-Jn|zK;7e6oOcS`# z-@t}p)r07zPr3D3&k17)p0^y~RUB>fi*&zD7()3~<^F^0`9-Bv5n7|3&JSmJ(WU$; zLI(cSgG8v5F*1##H5wbdm?x|E;j#q{0M{KevvijD77yP0B#OQA-C;4yNbF(vm=C>4 z?RsMSqOzeE$cs#1p?6X9gPdbzY%Hy*zUy1@3X)ka^7Lk*tfgB~uGGI5r(+&YlYki* z%58*;WD0|=cs*V-rr{AN7Q}jlj}W0|Z$rRXqKojP(>E zcNo~aRYVZy%A7z_-8B23xl#rhJODQ1-GkOuv=T&{2vI>|d<78`8cTsy?JC$e#9Sp$ z9yB{z*lC31_mnh0NYgaLr2&EBBD^mf-}9mC;%{sqjUem~Z1zcEC{QK`ITlJo`7?6d zR|}q`cYxCEp*rs zmDupnN^7Tv9UldI**jlzOUQR}3qge8;0RM~d``ZelE;WdqepBxWG7G) zo<{>4!PHyGJpdsB3~3kk({|h#sEfG%kJwVU1@gd&I8fxkzEe67#?P2k)WD4$WU6- znf6Vvut5&+#V~*FM_i(m{KltcJ>r~m_t;;T_9@rd3sRbFj^{@;TXk<2GlnqbT|Pxm zX{>XVG_6wZ;)s~<#BX+uRC0e01rWNHR4#60_l~YEDi;A+4_>PZ4aXokUSOlO{~dQW z%0roc_Owz5T}ZtQo?LURr?6cd!o6J*e!|b9ZywuDqL9Cfq3{4ngt@?Y(=MwK#0@7gTB0Y^9dhL5k|0gDP%gTFk}XL4@KFEQM%-ph(m{(jvlNYT z!CY4!In#HSz}|NkQZ5sa-3shHaC+-$c7iml98VC~iT}~I?6_l?d#P?$rD*kHsWPo; z)AzbVVA7Dbt1I-3PF~FDA>vCVWRnl*6QJ0~%qYF-{A|~BlXD;0=N-@r>=VCFFCosA zUj^*8+*}kp~${q{G-Bbxgdh{)ht7;=|TDTpgnRN5~t*?@|eE zO&Cik>cyEjQ8nMG@K#Q74--GS_J&WVV7OUd^lrvu<0X7SaqiiKND#07>W%cEiLc%f zsW<$`>Iun~*vvQX>2`G0CUQ0!;8Z*)At-n7G9J_ufW`?GEY0tmO04L~meR^lwH?l9 z*CX22m0HV+sE%m<_Um9_ySk#w<}#a?i7;OmqwFA|OvJh6TrWotsD47hpXvtGRm32$lWqKcr_(j`xV1Y6UJc%XdZLg7U+-b8VpF^lxe!}`;uAOZyh7gwS z@HONu6R|mSe4{yiG9e+#D_<0Azs-ex&(P}milgosjfOzZ8DDpv)uTH{zIVWU7=ZfAsxJfwc>*hpvm?zcuk({ z)?^BbNRgBm9%C&(d9dP56A^b$vMoabcZ?YhR~T5N5J5QpmRFz{)DqZdFi%gKsNzRJ z!tJo=p`~mrJ-J`Fd~WU(r661+D+Nz*dbGoX^uVv53!0Gtk$(s&M(ykgYlxrc+IJ}8 zLwiN*j)RKwdlwv0LhbMb_E5A`ZNgMeu*`z-#={0BP4FQvB@EUOk&8OJ44a;XPPX)y zW#|Al8#GGzDYCgD{&}g-Q#TV(59)C4@gDA?Pb1>GBuVZaR(QX~TJJ+61`&o@04IWJ z$=uR{;133b71uY>5WRy#Au|o8EI>t>%MmleR@?Ft{RC}d>v3-p6Q#xS4eE?ioAhTK zRN^Qb^fFSO=}RX!hV>^7JxGdg42L4por3}AkM^S48EyHeg1sXOD{y2uym?l>qiSOoXt`xLDK zurTp9Klq-ZKa8fkci(dl*_2dvMQo0JXS9AR&2Lt0y4+nCFw?}Y73|>=F?^skR;3`j zZm+QVa9Nthnr}*f}HDJ z>YxQGa6w929kE5_XR5m+^}KpCVyaV-LV{<8z8BKXS^cM71<8@H?<-PehhFmlXdKLN z{gvlMl%Kc=>8pkDdu!JTRn`<4bZlow@aTK#XhW>{%(sQSSYY9hnxJ(<~tufLpBGgbKHfx ziL7VYl;X`f74w=y(ZvGaI@<~6y(|JC#D$a|$WWU7G$j8I4hY zR-Yzu|4g&&ic-T=nN2Zk+&#xUoj&V+YJc(0_?~em`zcm6XE!)xa2?YG*m8{QcJy?v zULC7aX;t40#BLu$lYJcU;JHX;9`u4)VkBxWxs)d5+zZE0z6$OfI;=04({fGF6-+N& zJPK^qE#On_D^r&mS9>$vNDxr7KGy3$A zv4AIoF@`5uF+5s%I#g;X7RZ1Rf5e6U9hNrx5#A6DXr& z4Y|v@dFim5Ayv4}8?$kK4&Qus!~OjTE1fHko^4j&R&BEu41iU_7RRFYEs`R>G8m2x> zNA^SNX)R!C%*^-SRc$M{@oI#xngDU%d5&si_-Puq58dc=?t6H2Kz9$3U%gOn&2d^R zR;~es8(47C&yun{4`?A$NRR+La$JoZ6VVe-kJy(lvY*-pJ`E)q`1p)m{G7pJH9urYy41&GN{d3W^ z2FWUpk2b0n(HSV?eBe-)#za*@1s%iFo#ic_Rb~2+r1MVn#ZiK43~^qM_Dj5jkln&bK_LCVcRToj40XsxhZy!X8Trji@)XV-+7a2$7mMW2 zj3g^vqo}<;#G}~isw_)=iC!$#>J2H}0h&yVB?E)DKbrYM>Zt1u`3L**2O&=dy&JO{ z`hL1>=#vc%G0^hHPy)eK}1X z@X@6pC~8;HOwx#_*KxtNnNiA!C89t=0o6$ev+fg{1XmsXedW|w7;3O}wfK#fOpZ=h+tdKs6$wA{@6 zzIy+pap23HnU%R%`)zrYixg3|y{o?Huo>w#j~)#4BAuEFsTUgSy|;TCd2;1+U^Q|m zY~bV6^6TqHe6&Xi!%yQbA?#=v^J8NdYsU?D&ZfkfK_s5EIhSiMnu$?jASJo=AVs?E z=6U*prC5YSx1aNf0%!Yc&fU;mgoQKzLR%-^*pY|zC~M@r$%rFCTAT^YEp;zPn>q~k z9+UVTVrWx1kq&e{zj%$U5SrOwN_Mg*-T=JKJRNpmAr=aTk_c@(aDp};IJ$uuCdKdq8+HaDt;6?zUOAE6PCz%HJ)rKgvm@sZp0 zVvJUtW+XRe+1cgne8fI+Q*tiytPI`&yCTQ4A&^Qn-&5hSOA-E=Rd7I+F8PTb=b%sW zibat7&TD-}glL@%SibtXywaFdOGmt??{!F2=qV1&r5Jv8 zK&clEe1=;ZiYu00%o`)Zde0i=neudeijOxX8!ayRz%%DiFFtd&3C(PTXH|CKc!HQp zVf9CZP^=8iD^)5Fjp?$r^FDy4P@~6fDX5g;(ZW_XX%;@|)s}a3_Pol6N~uy;4nOKr zTN6SIs&1gsff*0&@48e{u{X{E6pYLX;ABV`Ow*mRo5x$p`VGv!tl%^?Tc_+cwCDO_ z##%h6NSA!8p<$Lqj;{(oc+;}pB{zkbI+|*X8|??D9NGE-sa&b=i|wXMd<|ZONoa2H z#23dQ+LVuclx7oixsMDxTF{zKJA(S@lb$6CZumlTn@x_6XC8@e>WPZ>1M3<3@gF=+Z)wGpOYl?R6E`jxNq4~L7gyG(lBfx9*Z7ZR6nLV4iWY?YZGnl zTgiDeOF=^+WzD~Nuhr=5GUy>(^{2YB7<)K0(FomM(l}?lx13Ji^X=I09(%q-D8g%O zQagh+Wm~4gDjy@)AeI=gsRyc7CVL*4)rBPNr3**WIRuV(e(%F(5ie8~3uaK5EI8CS zEFnPadb|Q3_(6z|vc5q)mwW)F|5e{6Clnfc_Hv7%x{09k{pBncA9|N_cCI z2|otx=UQ7OyPs-)X4CGsS=%K3cB-Dx7WU&+blZ)8mz-m7PY zk0?u~!KtPi@o~8m6_ZucJr%_qdVsJEbrwp{E0-fd5~D2JcJ&bW1Tw0Yl=59%y~^Lm z6;cy!D1f>7gri@?-EQUKo#}%L2D(FdJ+Cv8VRW21&p~64MKPj>1O^A!m2HP7#jFFK z%_SiB)_5*Nz^><&j+!#(W`|ALsH>V1EJb1hLa zHgIp+L(x4Ns?UJ8H)H`lsh@llxXAd3mb29&H>poOMKj+uHJ|Xat`rtc4a=;W;J4&4 zl6vfy?cbpHBt>o=RzxclVpHHj^FJfqj2_^}@n+q5`87~zE;qgO@zQ7^W~06NzNG(n z557m}8j1Mfn00Q5WDKt1{c19+S*Id1>7Cd40h(n~6JlekZy3aprp>5{C z3>$UrZJn8*-3bZK296iMztR~a6`5r@iv@YT9}w0ro>G6><;@qkWGaxZ^MQohcgg-D zRb=bMVqF5U({;)4znacNNvO&tR75_+!b)nvJp7Jz}x;rJPJnjAR0Y zod?);uZG&*@vJP%-*+EKn+{j0tZgu*6zlKrwT&2OP3g@=kp@gF-n*6yauDpvq7I$v zBOR}@3HG-Mx8+zSNV$h+YR?zUm^;cxz+8K_E7sQex1g5?s<@}eWNj1z*4;Q;(}v~ z>Tb?+1rNLxvQ&{p>ysrjHVG08HSJ^rmZ_cs_#g*P^fiEL!VkeC2Ig6Vl~F#Dww(2= zFQn&77`fvq>5rym46ZyZt9)F)?sz>`u49W~`^Z!EiMq35oLI^diOxk;P=0)|NquW_ zG75|GYC1X#cihr|^oGlycqBLL$eCmn@%*wz_~b$*JkA-u3t#L#v9BWI1N6f;4)!aj z2`pbL*A)E$jbq4DoJ9d8lJ^d72;KPdNwDy4Ago$)ATd2O<}g{vC38Ue<+6>=3{$*Z zEy`fbMf8a1bqb*s%+o3}LZvvB(fNi8yBeXE-J!i;l+PKG%}<_Ry7q0ka);Z}FoE_{ z!&ef#8+w&@XlHq!Fv^YXmRBmDyD@(*oGfzA@1fQQwja?s%z5}D&V$Oa0u%fdiF=*s z()Q-4)}yZ`Z~?D!3H)Z{ikx?2X)l=6d3z`YNtz(od8{lyz5|Epy*F>?USOss9}jF3Th zUqXQhK=cA}7j;4tP(NFuN6wX8kmEb>JBGAL@o)TR2pr-saEK>RHN+*Olf8kBp%K&X z$SxHA4fGJCYWRY|GDv%rsDfU7`^B%g!GbPOxWMm$&i#h5Cjrl z1A! zfh7Icg!w7i07=HJWW#?ZQo<{&X-?1;OHh)0TP<;D=&9&11FX(@3Ec{wPZbNSV9s7sm z!tV}@Us59EKw{;QxN}f3I`%II|BM~ykJz_=$G0=Ghe(%$$*rMa{}|do`=M`h=jP|} z-TAyr(LcHV*pRjO?t=+x79`!crS9K*_+vo-r^>_aB>5W&tosHE5B}ED?1v?#$p6;z zFQ-2msy||A`+-FBzmI{*?XMUpHh%~YL

4{-5ID`y-CfUucB?Cp6JN(BQXz#zsN$ zKj#SY!+I+PL5z5}+W7rtIC$}d<}X1A$o%>bF^FHY_Erkwr}dZ9JGT()2+eOucOCf; zL5Tlph%o%o5pyUomKPM$|Co}WE7QM~A%0GCa>$7vzC-#U5y9~LbkPtnN7NvkhWgoZ z13FJCR=@n1>OYofFp|%&9zMVXxA;Ik`N18!%j|#fhW^eC7)u=s!3@ ze;d*J{xW(+eQU%F_Vk5%aA)}+%%Hz5A+(@7cOSiGIDaJW2RrC4d;h@<`u#lon-=u< zRNWC=6aEES5&Dlc_veq=zZpVza}essRhgQ|Ega1)Ux@x!_>c>lf0vs5ZZ)d( s!wUSX@a$i Date: Tue, 19 May 2026 23:49:45 -0600 Subject: [PATCH 40/40] Add changelog for 4.2.0 --- .../serivesmejia/eocvsim/gui/DialogFactory.kt | 2 +- .../serivesmejia/eocvsim/gui/Visualizer.kt | 2 +- .../repository/PluginRepositoryManager.kt | 1 - README.md | 37 +++++++++++++++++++ 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt index 7d7a2847..a3a68516 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/DialogFactory.kt @@ -28,7 +28,7 @@ import javax.swing.filechooser.FileNameExtensionFilter class DialogFactory : KoinComponent { private val visualizer: Visualizer by inject() - private val pluginManager: org.deltacv.eocvsim.plugin.loader.PluginManager by inject() + private val pluginManager: PluginManager by inject() private val configManager: ConfigManager by inject() private val scope: CoroutineScope by inject() private val outputHandler: PluginOutputHandler by inject() diff --git a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt index d50814c1..376c3d8e 100644 --- a/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt +++ b/EOCV-Sim/src/main/java/com/github/serivesmejia/eocvsim/gui/Visualizer.kt @@ -243,7 +243,7 @@ class Visualizer : PhaseOrchestrableBase(), KoinComponent { inputSourceManager.onInputSourceInitError { dialogFactory.createInformation( frame, - "Error while loading requested source", "Falling back to previous source", + "Error while loading source.", "Falling back to previous source.", "Operation failed" ) } diff --git a/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt index 8ba79157..0f587604 100644 --- a/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt +++ b/EOCV-Sim/src/main/java/org/deltacv/eocvsim/plugin/repository/PluginRepositoryManager.kt @@ -178,7 +178,6 @@ class PluginRepositoryManager( } // Update the version - val oldLine = tomlLines[pluginLineIndex] tomlLines[pluginLineIndex] = "$pluginName = \"${artifact.groupId}:${artifact.artifactId}:${latest.version}\"" // Write updated content back to the TOML file diff --git a/README.md b/README.md index 2b939a35..8ba27e90 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,43 @@ 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.2.0 - Lifecycle & Platform Architecture Rework](https://github.com/deltacv/EOCV-Sim/releases/tag/v4.2.0) + +* This is the 37th release for EOCV-Sim + + * Major internal architecture rework focused on lifecycle management, startup orchestration and long-term platform maintainability + * Changelog + * **BREAKING**: Migrates package namespaces from io.github.deltacv to org.deltacv + * PaperVision is missing in this release and will be added in the next patch. + * Migrates OpenCV and webcam handling away from OpenPnP distributions into WPILib's OpenCV packaging and CSCore backend + * Adds support for Linux ARM64 and Intel macOS native distributions + * Replaces the legacy AprilTag plugin implementation with WPILib's AprilTag support while preserving backwards compatibility + * Adds multiplatform releases for: + * Windows x86-64 + * Linux x86-64 + * Linux ARM64 + * macOS Intel + * macOS Apple Silicon + * Adds a Bootstrap launcher that validates the Java runtime before startup instead of silently failing + * Bootstrap now scans common Java installation locations on Windows to help users locate compatible runtimes + * Adds timeout configuration controls for webcams + * Refactors the tunable field API into a cleaner and more maintainable implementation + * Implements individual APIs for accessing visualizer component controls + * Internal changes: + * Redesigns initialization and lifecycle handling around a centralized orchestrator with phased startup/shutdown + * Centralizes EOCV-Sim lifecycle scopes and improves cancellation handling across the application + * Adds support for non-blocking cancellation of input sources + * Migrates major internal systems and managers to Kotlin + * Migrates dependency injection to Koin + * Rewrites InputSourceManager and Visualizer into Kotlin + * Replaces Gson usage with Jackson serialization APIs + * Updates GitHub workflows to Java 25 + * Consolidates Maven publishing and multiplatform workflow handling + * Bugfixes: + * Fixes cases where onDrawFrame could be called before initialization completed + * Fixes continuation handling edge cases in the plugin manager + * Improves workflow artifact publishing reliability across platforms + ## [v4.1.2 - PaperVision Stable Release](https://github.com/deltacv/EOCV-Sim/releases/tag/v4.1.2) - This is the 36th release for EOCV-Sim - Adds [PaperVision v1.1.0](https://github.com/deltacv/PaperVision/releases/tag/v1.1.0)