diff --git a/src/main/kotlin/solve/rendering/canvas/OpenGLCanvas.kt b/src/main/kotlin/solve/rendering/canvas/OpenGLCanvas.kt index 5f584749..22ff0e8c 100644 --- a/src/main/kotlin/solve/rendering/canvas/OpenGLCanvas.kt +++ b/src/main/kotlin/solve/rendering/canvas/OpenGLCanvas.kt @@ -30,6 +30,9 @@ import solve.rendering.engine.utils.plus import solve.rendering.engine.utils.toFloatVector import com.huskerdev.openglfx.canvas.GLCanvas as OpenGLFXCanvas +/** + * Encapsulates initialization logic of the OpenGLFX library component. + */ abstract class OpenGLCanvas { val canvas: OpenGLFXCanvas = OpenGLFXCanvas(LWJGLExecutor.LWJGL_MODULE, flipY = true) diff --git a/src/main/kotlin/solve/rendering/canvas/SceneCanvas.kt b/src/main/kotlin/solve/rendering/canvas/SceneCanvas.kt index 07f7c82a..e721c95d 100644 --- a/src/main/kotlin/solve/rendering/canvas/SceneCanvas.kt +++ b/src/main/kotlin/solve/rendering/canvas/SceneCanvas.kt @@ -41,7 +41,17 @@ import solve.utils.structures.DoublePoint import tornadofx.* import solve.rendering.engine.scene.Scene as EngineScene +/** + * Aggregates logic related to scene control. + * Provides interface for managing frames and changing visualization settings. + */ class SceneCanvas : OpenGLCanvas() { + val displayScale: Float + get() = canvas.scene?.window?.renderScaleX?.toFloat() ?: 1f + + val scaledIdentityFramesSizeScale: Float + get() = IdentityFramesSizeScale * displayScale + private var sceneController: SceneController? = null private var engineScene: EngineScene? = null @@ -78,6 +88,7 @@ class SceneCanvas : OpenGLCanvas() { init { initializeCanvasEvents() + ServiceLocator.registerService(this) } fun setNewScene(scene: Scene) { @@ -158,8 +169,8 @@ class SceneCanvas : OpenGLCanvas() { val canvasScreenPosition = canvas.getScreenPosition() contextMenu.show( canvas.scene.window, - canvasScreenPosition.x + screenPoint.x.toDouble(), - canvasScreenPosition.y + screenPoint.y.toDouble() + canvasScreenPosition.x + screenPoint.x.toDouble() / displayScale, + canvasScreenPosition.y + screenPoint.y.toDouble() / displayScale ) } } @@ -446,7 +457,7 @@ class SceneCanvas : OpenGLCanvas() { ) } val getScreenScale = { - window.camera.zoom.toDouble() / IdentityFramesSizeScale + window.camera.zoom.toDouble() / scaledIdentityFramesSizeScale } val associationAdorner = AssociationAdorner( framesSize.x.toDouble(), diff --git a/src/main/kotlin/solve/rendering/engine/camera/Camera.kt b/src/main/kotlin/solve/rendering/engine/camera/Camera.kt index fe7d109c..46148d99 100644 --- a/src/main/kotlin/solve/rendering/engine/camera/Camera.kt +++ b/src/main/kotlin/solve/rendering/engine/camera/Camera.kt @@ -7,6 +7,10 @@ import solve.rendering.engine.utils.plus import solve.rendering.engine.utils.times import solve.rendering.engine.utils.toFloatVector +/** + * Used to move around the scene and scale it. + * Provides methods for calculating projection matrices that are used in shaders. + */ class Camera(var position: Vector2f = Vector2f(), zoom: Float = 1f) { var zoom: Float = zoom private set(value) { diff --git a/src/main/kotlin/solve/rendering/engine/components/Sprite.kt b/src/main/kotlin/solve/rendering/engine/components/Sprite.kt deleted file mode 100644 index c2dc9829..00000000 --- a/src/main/kotlin/solve/rendering/engine/components/Sprite.kt +++ /dev/null @@ -1,32 +0,0 @@ -package solve.rendering.engine.components - -import org.joml.Vector2f -import solve.rendering.engine.core.texture.Texture2D - -class Sprite(val texture: Texture2D, uvCoordinates: List = defaultUVCoordinates) { - val uvCoordinates: List - - val width: Int - get() = texture.width - val height: Int - get() = texture.height - - init { - if (uvCoordinates.count() != UVCoordinatesNumber) { - println("The number of the uv coordinates is incorrect!") - this.uvCoordinates = defaultUVCoordinates - } else { - this.uvCoordinates = uvCoordinates - } - } - - companion object { - private const val UVCoordinatesNumber = 4 - private val defaultUVCoordinates = listOf( - Vector2f(0f, 0f), - Vector2f(0f, 1f), - Vector2f(1f, 1f), - Vector2f(1f, 0f) - ) - } -} diff --git a/src/main/kotlin/solve/rendering/engine/components/SpriteRenderer.kt b/src/main/kotlin/solve/rendering/engine/components/SpriteRenderer.kt deleted file mode 100644 index 4a15ff3f..00000000 --- a/src/main/kotlin/solve/rendering/engine/components/SpriteRenderer.kt +++ /dev/null @@ -1,37 +0,0 @@ -package solve.rendering.engine.components - -import solve.rendering.engine.core.texture.Texture2D -import solve.rendering.engine.scene.Transform -import solve.rendering.engine.structures.Color - -class SpriteRenderer(val transform: Transform = Transform()) { - var color = Color.white - private set - var sprite: Sprite? = null - private set - - val texture: Texture2D? - get() = sprite?.texture - - constructor(texture: Texture2D) : this() { - setTexture(texture) - } - - constructor(sprite: Sprite) : this() { - setSprite(sprite) - } - - fun setColor(color: Color) { - this.color = color - } - - fun setSprite(sprite: Sprite) { - this.sprite = sprite - } - - fun setTexture(texture: Texture2D) { - if (sprite?.texture != texture) { - sprite = Sprite(texture) - } - } -} diff --git a/src/main/kotlin/solve/rendering/engine/core/batch/PrimitiveType.kt b/src/main/kotlin/solve/rendering/engine/core/batch/PrimitiveType.kt index 4d3c3e7c..2864c056 100644 --- a/src/main/kotlin/solve/rendering/engine/core/batch/PrimitiveType.kt +++ b/src/main/kotlin/solve/rendering/engine/core/batch/PrimitiveType.kt @@ -4,6 +4,9 @@ import org.lwjgl.opengl.GL11.GL_LINES import org.lwjgl.opengl.GL11.GL_POINTS import org.lwjgl.opengl.GL11.GL_TRIANGLES +/** + * Used to draw with EBO in OpenGL. + */ enum class PrimitiveType( val verticesNumber: Int, val openGLPrimitive: Int, diff --git a/src/main/kotlin/solve/rendering/engine/core/batch/RenderBatch.kt b/src/main/kotlin/solve/rendering/engine/core/batch/RenderBatch.kt index 6d033a9d..b28949e1 100644 --- a/src/main/kotlin/solve/rendering/engine/core/batch/RenderBatch.kt +++ b/src/main/kotlin/solve/rendering/engine/core/batch/RenderBatch.kt @@ -22,6 +22,13 @@ import solve.rendering.engine.core.texture.Texture import solve.rendering.engine.shader.ShaderAttributeType import solve.rendering.engine.utils.toList +/** + * Used to draw similar objects in a single draw call. + * @param maxBatchSize maximum allowed number of the objects in the batch. + * @param zIndex depth index of the batch. + * @param primitiveType used to determine the type of the drawing primitives in EBO. + * @param attributes used to specify the attributes of each vertex. + */ open class RenderBatch( private val maxBatchSize: Int, val zIndex: Int, @@ -58,25 +65,30 @@ open class RenderBatch( textures.clear() } + // Compares batches according to their depth. override fun compareTo(other: RenderBatch): Int = batchComparator.compare(this, other) + // Binds vertices data, attributes data and textures before the rendering. fun bind() { glBindVertexArray(vaoID) attributes.forEachIndexed { index, _ -> glEnableVertexAttribArray(index) } textures.forEachIndexed { index, texture -> texture.bindToSlot(index + 1) } } + // Unbinds vertices data, attributes data and textures after the rendering. fun unbind() { attributes.forEachIndexed { index, _ -> glDisableVertexAttribArray(index) } textures.forEach { texture -> texture.unbind() } glBindVertexArray(0) } + // Cleans the vertices data buffer. fun rebuffer() { glBindBuffer(GL_ARRAY_BUFFER, vboID) glBufferSubData(GL_ARRAY_BUFFER, 0, verticesDataBuffer) } + // Deletes the batch buffers. fun deleteBuffers() { glDeleteBuffers(vboID) glDeleteBuffers(eboID) @@ -150,6 +162,7 @@ open class RenderBatch( } } + // Used to generate vertex indices to specify an order of their drawing. private fun generateElementsIndices(): IntArray { val elementsBuffer = IntArray(maxBatchSize * primitiveType.drawingOrderElementsNumber) for (i in 0 until maxBatchSize) { @@ -190,7 +203,7 @@ open class RenderBatch( } companion object { - private const val MaxTexturesNumber = 7 + const val MaxTexturesNumber = 7 private val batchComparator = Comparator.comparingInt { it.zIndex } } diff --git a/src/main/kotlin/solve/rendering/engine/core/input/MouseInputHandler.kt b/src/main/kotlin/solve/rendering/engine/core/input/MouseInputHandler.kt index f005f700..f296ee7e 100644 --- a/src/main/kotlin/solve/rendering/engine/core/input/MouseInputHandler.kt +++ b/src/main/kotlin/solve/rendering/engine/core/input/MouseInputHandler.kt @@ -1,15 +1,15 @@ package solve.rendering.engine.core.input -import javafx.scene.robot.Robot import org.joml.Vector2i import solve.rendering.engine.utils.minus import solve.rendering.engine.utils.pointToSegmentDistance import solve.rendering.engine.utils.toFloatVector import solve.rendering.engine.utils.toVector2i import solve.scene.model.Landmark -import java.awt.Color -import java.awt.MouseInfo +/** + * Used to handle mouse click on landmarks. + */ object MouseInputHandler { private const val LineHandledDistanceMultiplier = 4f private const val PointHandledDistanceMultiplier = 1.5f @@ -62,17 +62,4 @@ object MouseInputHandler { return minKeypointDistanceIndex } - - fun getClickedPixelColor(): Color { - val pointerLocation = MouseInfo.getPointerInfo().location - val robot = Robot() - - val javaFXColor = robot.getPixelColor(pointerLocation.x.toDouble(), pointerLocation.y.toDouble()) - - return Color( - (javaFXColor.red * ColorSegmentMaxValue).toInt(), - (javaFXColor.green * ColorSegmentMaxValue).toInt(), - (javaFXColor.blue * ColorSegmentMaxValue).toInt() - ) - } } diff --git a/src/main/kotlin/solve/rendering/engine/core/renderers/FramesRenderer.kt b/src/main/kotlin/solve/rendering/engine/core/renderers/FramesRenderer.kt index a6427379..90ae555f 100644 --- a/src/main/kotlin/solve/rendering/engine/core/renderers/FramesRenderer.kt +++ b/src/main/kotlin/solve/rendering/engine/core/renderers/FramesRenderer.kt @@ -33,6 +33,10 @@ import kotlin.math.abs import kotlin.math.max import kotlin.math.min +/** + * Used to draw the frames. + * Encapsulates logic to provide virtualization at the data layer.* + */ class FramesRenderer( window: Window ) : Renderer(window) { @@ -165,6 +169,7 @@ class FramesRenderer( } } + // Updates the buffers with actual frames textures. private fun updateBuffersTextures() { val cameraGridCellPosition = getScreenTopLeftGridCellPosition() if (cameraGridCellPosition != cameraLastGridCellPosition) { @@ -173,6 +178,7 @@ class FramesRenderer( cameraLastGridCellPosition = cameraGridCellPosition } + // Defines textures that became visible in the last draw call and uploads them to the buffers. private fun loadNewTexturesToBuffers(cameraGridCellPosition: Vector2i) { val cameraPosition = Vector2i(cameraGridCellPosition) cameraPosition.x.coerceIn(0 until gridWidth) @@ -226,6 +232,7 @@ class FramesRenderer( return framesRect } + // Uploads the rect of frames (that was taken from frames selection) to the buffers. private fun loadRectFramesToBuffers(framesRect: IntRect) { val rectFrames = getFramesAtRect(framesRect) @@ -250,6 +257,7 @@ class FramesRenderer( } } + // Returns the integer position of the frame cell located in the top-left corner of the screen. private fun getScreenTopLeftGridCellPosition(): Vector2i { val cameraGridCellPosition = getCameraCellPosition() return (cameraGridCellPosition - Vector2f(buffersSize) / 2f).toIntVector() diff --git a/src/main/kotlin/solve/rendering/engine/core/renderers/LandmarkLayerRenderer.kt b/src/main/kotlin/solve/rendering/engine/core/renderers/LandmarkLayerRenderer.kt index 218f973c..7e3390f0 100644 --- a/src/main/kotlin/solve/rendering/engine/core/renderers/LandmarkLayerRenderer.kt +++ b/src/main/kotlin/solve/rendering/engine/core/renderers/LandmarkLayerRenderer.kt @@ -8,6 +8,10 @@ import solve.scene.model.Landmark import solve.scene.model.Layer import solve.scene.model.Scene +/** + * The base class for all landmarks renderers. + * Encapsulates logic to provide virtualization at the data layer. + */ abstract class LandmarkLayerRenderer( window: Window, protected val getScene: () -> Scene? diff --git a/src/main/kotlin/solve/rendering/engine/core/renderers/PointAssociationsRenderer.kt b/src/main/kotlin/solve/rendering/engine/core/renderers/PointAssociationsRenderer.kt index 5c18a684..0cb545f4 100644 --- a/src/main/kotlin/solve/rendering/engine/core/renderers/PointAssociationsRenderer.kt +++ b/src/main/kotlin/solve/rendering/engine/core/renderers/PointAssociationsRenderer.kt @@ -18,6 +18,9 @@ import solve.rendering.engine.utils.toVector2i import solve.scene.view.association.AssociationManager import kotlin.math.min +/** + * Used to draw points associations. + */ class PointAssociationsRenderer( window: Window, private val associationManager: AssociationManager diff --git a/src/main/kotlin/solve/rendering/engine/core/renderers/Renderer.kt b/src/main/kotlin/solve/rendering/engine/core/renderers/Renderer.kt index fa0c9af2..998127cd 100644 --- a/src/main/kotlin/solve/rendering/engine/core/renderers/Renderer.kt +++ b/src/main/kotlin/solve/rendering/engine/core/renderers/Renderer.kt @@ -14,6 +14,10 @@ import solve.scene.model.VisualizationFrame import solve.utils.ceilToInt import kotlin.math.min +/** + * The base class for all renderers. + * Provides methods for managing shaders and controlling the rendering process + */ abstract class Renderer(protected val window: Window) : Comparable { protected abstract val maxBatchSize: Int diff --git a/src/main/kotlin/solve/rendering/engine/core/texture/ArrayTexture.kt b/src/main/kotlin/solve/rendering/engine/core/texture/ArrayTexture.kt index 7d6bd081..bcead802 100644 --- a/src/main/kotlin/solve/rendering/engine/core/texture/ArrayTexture.kt +++ b/src/main/kotlin/solve/rendering/engine/core/texture/ArrayTexture.kt @@ -11,6 +11,9 @@ import org.lwjgl.opengl.GL30.GL_TEXTURE_2D_ARRAY import org.lwjgl.opengl.GL30.glGenerateMipmap import java.nio.ByteBuffer +/** + * Used to store a large number of textures of the same size. + */ class ArrayTexture( width: Int, height: Int, diff --git a/src/main/kotlin/solve/rendering/engine/core/texture/Texture.kt b/src/main/kotlin/solve/rendering/engine/core/texture/Texture.kt index c0e987f8..7aef6149 100644 --- a/src/main/kotlin/solve/rendering/engine/core/texture/Texture.kt +++ b/src/main/kotlin/solve/rendering/engine/core/texture/Texture.kt @@ -17,6 +17,10 @@ enum class TextureFilterType(val openGLParamValue: Int) { Smoothed(GL_LINEAR) } +/** + * The base class for all textures. + * Provides an interface for creating, binding and deleting. + */ abstract class Texture(private val filterType: TextureFilterType = TextureFilterType.Smoothed) { protected abstract val textureOpenGLType: Int diff --git a/src/main/kotlin/solve/rendering/engine/core/texture/Texture2D.kt b/src/main/kotlin/solve/rendering/engine/core/texture/Texture2D.kt index 66eac86f..5c0254e6 100644 --- a/src/main/kotlin/solve/rendering/engine/core/texture/Texture2D.kt +++ b/src/main/kotlin/solve/rendering/engine/core/texture/Texture2D.kt @@ -11,6 +11,9 @@ import org.lwjgl.stb.STBImage.stbi_image_free import org.lwjgl.stb.STBImage.stbi_load import org.lwjgl.stb.STBImage.stbi_set_flip_vertically_on_load +/** + * Used to store a default 2D texture. + */ class Texture2D( private val filePath: String, filterType: TextureFilterType = TextureFilterType.Smoothed diff --git a/src/main/kotlin/solve/rendering/engine/core/texture/Texture2DData.kt b/src/main/kotlin/solve/rendering/engine/core/texture/Texture2DData.kt index bc6f52e2..f068b243 100644 --- a/src/main/kotlin/solve/rendering/engine/core/texture/Texture2DData.kt +++ b/src/main/kotlin/solve/rendering/engine/core/texture/Texture2DData.kt @@ -2,6 +2,9 @@ package solve.rendering.engine.core.texture import java.nio.ByteBuffer +/** + * Used to store a texture data before it can be loaded to the GPU memory. + */ class Texture2DData( val data: ByteBuffer, val width: Int, diff --git a/src/main/kotlin/solve/rendering/engine/core/texture/TextureChannelsType.kt b/src/main/kotlin/solve/rendering/engine/core/texture/TextureChannelsType.kt index 50e9b16d..aa33cd0c 100644 --- a/src/main/kotlin/solve/rendering/engine/core/texture/TextureChannelsType.kt +++ b/src/main/kotlin/solve/rendering/engine/core/texture/TextureChannelsType.kt @@ -3,6 +3,9 @@ package solve.rendering.engine.core.texture import org.lwjgl.opengl.GL11.GL_RGB import org.lwjgl.opengl.GL11.GL_RGBA +/** + * Used to store a type of color encoding of the texture. + */ enum class TextureChannelsType(val channelsNumber: Int, val openGLType: Int) { RGB(3, GL_RGB), RGBA(4, GL_RGBA); diff --git a/src/main/kotlin/solve/rendering/engine/scene/Scene.kt b/src/main/kotlin/solve/rendering/engine/scene/Scene.kt index 68ef8b7c..5ac96f20 100644 --- a/src/main/kotlin/solve/rendering/engine/scene/Scene.kt +++ b/src/main/kotlin/solve/rendering/engine/scene/Scene.kt @@ -14,6 +14,9 @@ import solve.scene.model.Scene import solve.scene.model.VisualizationFrame import java.util.concurrent.CopyOnWriteArrayList +/** + * Aggregates all renderers of the application scene. + */ class Scene( val framesRenderer: FramesRenderer, val pointAssociationsRenderer: PointAssociationsRenderer, diff --git a/src/main/kotlin/solve/rendering/engine/scene/Transform.kt b/src/main/kotlin/solve/rendering/engine/scene/Transform.kt deleted file mode 100644 index fde69d6c..00000000 --- a/src/main/kotlin/solve/rendering/engine/scene/Transform.kt +++ /dev/null @@ -1,10 +0,0 @@ -package solve.rendering.engine.scene - -import org.joml.Vector2f - -data class Transform( - val position: Vector2f = Vector2f(), - val rotation: Float = 0f, - val scale: Vector2f = Vector2f(1f), - val zIndex: Int = 0 -) diff --git a/src/main/kotlin/solve/rendering/engine/shader/ShaderProgram.kt b/src/main/kotlin/solve/rendering/engine/shader/ShaderProgram.kt index 9e558933..a258da84 100644 --- a/src/main/kotlin/solve/rendering/engine/shader/ShaderProgram.kt +++ b/src/main/kotlin/solve/rendering/engine/shader/ShaderProgram.kt @@ -45,6 +45,9 @@ import org.lwjgl.opengl.GL20.glValidateProgram import solve.rendering.engine.shader.ShaderType.Companion.getShaderTypeID import solve.utils.readResourcesFileText +/** + * Used to create, link and manage shaders. + */ class ShaderProgram { private val shaderProgramID: Int = glCreateProgram() diff --git a/src/main/kotlin/solve/scene/controller/SceneController.kt b/src/main/kotlin/solve/scene/controller/SceneController.kt index 7c6970d1..b01f58cf 100644 --- a/src/main/kotlin/solve/scene/controller/SceneController.kt +++ b/src/main/kotlin/solve/scene/controller/SceneController.kt @@ -198,7 +198,7 @@ class SceneController : Controller() { max( installedMinScale, sceneWidthProperty.value / ((scene.frameSize.width + SceneView.framesMargin) * columnsNumber) * - SceneCanvas.IdentityFramesSizeScale + (ServiceLocator.getService()?.scaledIdentityFramesSizeScale ?: 1f) ), DefaultMaxScale ) diff --git a/src/main/kotlin/solve/scene/model/Scene.kt b/src/main/kotlin/solve/scene/model/Scene.kt index bc194359..533fd73d 100644 --- a/src/main/kotlin/solve/scene/model/Scene.kt +++ b/src/main/kotlin/solve/scene/model/Scene.kt @@ -9,6 +9,7 @@ import solve.utils.structures.Size as DoubleSize * * @param frames images with related landmarks selected in the catalog {@link CatalogueController}. * @param layerSettings information about layers in the project, used to determine current order of a layer. + * @param layerStates stores current layers states. */ class Scene( val frames: List, diff --git a/src/main/kotlin/solve/scene/view/SceneView.kt b/src/main/kotlin/solve/scene/view/SceneView.kt index 86c6b241..0d93d287 100644 --- a/src/main/kotlin/solve/scene/view/SceneView.kt +++ b/src/main/kotlin/solve/scene/view/SceneView.kt @@ -3,6 +3,7 @@ package solve.scene.view import javafx.application.Platform import javafx.beans.InvalidationListener import javafx.scene.input.MouseEvent +import javafx.stage.Screen import org.joml.Vector2i import solve.rendering.canvas.SceneCanvas import solve.rendering.engine.core.input.MouseButton @@ -38,8 +39,13 @@ class SceneView : View() { addBindings() } - private fun extrudeEventMousePosition(event: MouseEvent) = Vector2i(event.x.toInt(), event.y.toInt()) - + private fun extrudeEventMousePosition(event: MouseEvent): Vector2i { + val screen = Screen.getPrimary() + return Vector2i( + (event.x * screen.outputScaleX).toInt(), + (event.y * screen.outputScaleY).toInt() + ) + } private fun addBindings() { addSceneParamsBindings() addSceneFramesBindings() diff --git a/src/main/kotlin/solve/scene/view/association/AssociationManager.kt b/src/main/kotlin/solve/scene/view/association/AssociationManager.kt index 2fb6ac17..42b42639 100644 --- a/src/main/kotlin/solve/scene/view/association/AssociationManager.kt +++ b/src/main/kotlin/solve/scene/view/association/AssociationManager.kt @@ -7,7 +7,7 @@ class AssociationManager { private var framesSelection = listOf() private val _associationConnections = mutableListOf() val associationConnections: List - get() = _associationConnections + get() = _associationConnections.toList() fun setFramesSelection(framesSelection: List) { this.framesSelection = framesSelection diff --git a/src/test/kotlin/solve/unit/rendering/engine/core/input/MouseInputHandlerTests.kt b/src/test/kotlin/solve/unit/rendering/engine/core/input/MouseInputHandlerTests.kt new file mode 100644 index 00000000..2e37f1d5 --- /dev/null +++ b/src/test/kotlin/solve/unit/rendering/engine/core/input/MouseInputHandlerTests.kt @@ -0,0 +1,104 @@ +package solve.unit.rendering.engine.core.input + +import junit.framework.TestCase.assertEquals +import org.joml.Vector2i +import org.junit.jupiter.api.Test +import solve.rendering.engine.core.input.MouseInputHandler.indexOfClickedLineLandmark +import solve.rendering.engine.core.input.MouseInputHandler.indexOfClickedPointLandmark +import solve.scene.model.ColorManager +import solve.scene.model.Landmark +import solve.scene.model.LayerSettings +import solve.scene.model.LayerState +import solve.scene.model.Point + +class MouseInputHandlerTests { + @Test + fun `Getting the index of the clicked line landmark (1st test)`() { + val clickedLineIndex = indexOfClickedLineLandmark(testLineLandmarks, Vector2i(10, 3), 1f) + assertEquals(0, clickedLineIndex) + } + + @Test + fun `Getting the index of the clicked line landmark (2nd test)`() { + val clickedLineIndex = indexOfClickedLineLandmark(testLineLandmarks, Vector2i(23, 9), 1f) + assertEquals(5, clickedLineIndex) + } + + @Test + fun `Getting the index of the clicked line landmark (3rd test)`() { + val clickedLineIndex = indexOfClickedLineLandmark(testLineLandmarks, Vector2i(14, 19), 1f) + assertEquals(4, clickedLineIndex) + } + + @Test + fun `Getting the index of the clicked line landmark (4th test)`() { + val clickedLineIndex = indexOfClickedLineLandmark(testLineLandmarks, Vector2i(33, 16), 1f) + assertEquals(6, clickedLineIndex) + } + + @Test + fun `Trying to get the index of the non-existing line landmark`() { + val clickedLineIndex = indexOfClickedLineLandmark(testLineLandmarks, Vector2i(25, 26), 1f) + assertEquals(-1, clickedLineIndex) + } + + @Test + fun `Getting the index of the clicked point landmark (1st test)`() { + val clickedPointIndex = indexOfClickedPointLandmark(testPointLandmarks, Vector2i(8, 13), 1f) + assertEquals(5, clickedPointIndex) + } + + @Test + fun `Getting the index of the clicked point landmark (2nd test)`() { + val clickedPointIndex = indexOfClickedPointLandmark(testPointLandmarks, Vector2i(21, 16), 1f) + assertEquals(8, clickedPointIndex) + } + + @Test + fun `Getting the index of the clicked point landmark (3rd test)`() { + val clickedPointIndex = indexOfClickedPointLandmark(testPointLandmarks, Vector2i(26, 19), 1f) + assertEquals(11, clickedPointIndex) + } + + companion object { + val testLinesLayerSettings = LayerSettings.LineLayerSettings( + "test_lines_layer", + "", + ColorManager() + ) + val testLinesLayerState = LayerState("test_lines_layer_state") + val testLineLandmarks = listOf( + Landmark.Line(0L, testLinesLayerSettings, testLinesLayerState, Point(4, 2), Point(31, 9)), + Landmark.Line(1L, testLinesLayerSettings, testLinesLayerState, Point(11, 3), Point(9, 16)), + Landmark.Line(2L, testLinesLayerSettings, testLinesLayerState, Point(5, 30), Point(31, 30)), + Landmark.Line(3L, testLinesLayerSettings, testLinesLayerState, Point(23, 15), Point(28, 18)), + Landmark.Line(4L, testLinesLayerSettings, testLinesLayerState, Point(8, 10), Point(19, 26)), + Landmark.Line(5L, testLinesLayerSettings, testLinesLayerState, Point(3, 17), Point(33, 5)), + Landmark.Line(6L, testLinesLayerSettings, testLinesLayerState, Point(33, 16), Point(30, 24)) + ) + + val testPointsLayerSettings = LayerSettings.PointLayerSettings( + "test_points_layer", + "", + ColorManager() + ) + val testPointsLayerState = LayerState("test_points_layer_state") + val testPointLandmarks = listOf( + Landmark.Keypoint(0L, testPointsLayerSettings, testPointsLayerState, Point(6, 3)), + Landmark.Keypoint(1L, testPointsLayerSettings, testPointsLayerState, Point(18, 5)), + Landmark.Keypoint(2L, testPointsLayerSettings, testPointsLayerState, Point(32, 4)), + Landmark.Keypoint(3L, testPointsLayerSettings, testPointsLayerState, Point(26, 10)), + Landmark.Keypoint(4L, testPointsLayerSettings, testPointsLayerState, Point(32, 10)), + Landmark.Keypoint(5L, testPointsLayerSettings, testPointsLayerState, Point(8, 13)), + Landmark.Keypoint(6L, testPointsLayerSettings, testPointsLayerState, Point(17, 14)), + Landmark.Keypoint(7L, testPointsLayerSettings, testPointsLayerState, Point(8, 15)), + Landmark.Keypoint(8L, testPointsLayerSettings, testPointsLayerState, Point(20, 16)), + Landmark.Keypoint(9L, testPointsLayerSettings, testPointsLayerState, Point(28, 19)), + Landmark.Keypoint(10L, testPointsLayerSettings, testPointsLayerState, Point(6, 20)), + Landmark.Keypoint(11L, testPointsLayerSettings, testPointsLayerState, Point(26, 20)), + Landmark.Keypoint(12L, testPointsLayerSettings, testPointsLayerState, Point(13, 26)), + Landmark.Keypoint(13L, testPointsLayerSettings, testPointsLayerState, Point(28, 19)), + Landmark.Keypoint(14L, testPointsLayerSettings, testPointsLayerState, Point(29, 31)) + ) + } +} diff --git a/src/test/kotlin/solve/unit/rendering/engine/structures/ColorTests.kt b/src/test/kotlin/solve/unit/rendering/engine/structures/ColorTests.kt index bbd63b41..f382421d 100644 --- a/src/test/kotlin/solve/unit/rendering/engine/structures/ColorTests.kt +++ b/src/test/kotlin/solve/unit/rendering/engine/structures/ColorTests.kt @@ -19,6 +19,13 @@ internal class ColorTests { assertEquals(testColor, color) } + @Test + fun `Compares same colors`() { + val firstColor = Color(0.5f, 1f, 0.25f, 1f) + val secondColor = Color(0.5f, 1f, 0.25f, 1f) + assert(firstColor == secondColor) + } + companion object { val testColor = Color(0.5f, 0.25f, 0.75f, 0.9f) } diff --git a/src/test/kotlin/solve/unit/rendering/engine/utils/CalculationUtilsTests.kt b/src/test/kotlin/solve/unit/rendering/engine/utils/CalculationUtilsTests.kt new file mode 100644 index 00000000..88f498be --- /dev/null +++ b/src/test/kotlin/solve/unit/rendering/engine/utils/CalculationUtilsTests.kt @@ -0,0 +1,64 @@ +package solve.unit.rendering.engine.utils + +import org.joml.Vector2f +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import solve.rendering.engine.utils.pointToSegmentDistance +import solve.rendering.engine.utils.pointToSegmentDistanceSquared + +internal class CalculationUtilsTests { + @Test + fun `Calculates the square distance from the point to the line segment (1st test)`() { + val pointPosition = Vector2f(6f, 1f) + val segmentStartPosition = Vector2f(1f, 3f) + val segmentEndPosition = Vector2f(1f, 5f) + assertEquals( + 29f, + pointToSegmentDistanceSquared(pointPosition, segmentStartPosition, segmentEndPosition) + ) + } + + @Test + fun `Calculates the square distance from the point to the line segment (2nd test)`() { + val pointPosition = Vector2f(4f, 0f) + val segmentStartPosition = Vector2f(5f, 1f) + val segmentEndPosition = Vector2f(8f, 4f) + assertEquals( + 2f, + pointToSegmentDistanceSquared(pointPosition, segmentStartPosition, segmentEndPosition) + ) + } + + @Test + fun `Calculates the square distance from the point to the line segment (3rd test)`() { + val pointPosition = Vector2f(2f, -2f) + val segmentStartPosition = Vector2f(-1f, -3f) + val segmentEndPosition = Vector2f(5f, -1f) + assertEquals( + 0f, + pointToSegmentDistanceSquared(pointPosition, segmentStartPosition, segmentEndPosition) + ) + } + + @Test + fun `Calculates the square distance from the point to the line segment (4th test)`() { + val pointPosition = Vector2f(2f, 6f) + val segmentStartPosition = Vector2f(-2f, 4f) + val segmentEndPosition = Vector2f(4f, 2f) + assertEquals( + 10f, + pointToSegmentDistanceSquared(pointPosition, segmentStartPosition, segmentEndPosition) + ) + } + + @Test + fun `Calculates the distance from the point to the line segment`() { + val pointPosition = Vector2f(2f, -5f) + val segmentStartPosition = Vector2f(-2f, -2f) + val segmentEndPosition = Vector2f(-2f, -7f) + assertEquals( + 4f, + pointToSegmentDistance(pointPosition, segmentStartPosition, segmentEndPosition) + ) + } +} diff --git a/src/test/kotlin/solve/unit/scene/view/association/AssociationManagerTests.kt b/src/test/kotlin/solve/unit/scene/view/association/AssociationManagerTests.kt new file mode 100644 index 00000000..247d539c --- /dev/null +++ b/src/test/kotlin/solve/unit/scene/view/association/AssociationManagerTests.kt @@ -0,0 +1,126 @@ +package solve.unit.scene.view.association + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import solve.scene.model.ColorManager +import solve.scene.model.Layer +import solve.scene.model.LayerSettings +import solve.scene.model.VisualizationFrame +import solve.scene.view.association.AssociationManager +import kotlin.io.path.Path + +internal class AssociationManagerTests { + @Test + fun `Associates two frames`() { + val associationManager = AssociationManager() + associationManager.setFramesSelection(testFrames) + associationManager.associate(0, 1) + assert( + associationManager.associationConnections.any { + it.firstFrameIndex == 0 && it.secondFrameIndex == 1 + } + ) + } + + @Test + fun `Associate three frames transitively`() { + val associationManager = AssociationManager() + associationManager.setFramesSelection(testFrames) + associationManager.associate(0, 1) + associationManager.associate(1, 2) + associationManager.associate(2, 0) + assert( + associationManager.associationConnections.any { + it.firstFrameIndex == 0 && it.secondFrameIndex == 1 + } && associationManager.associationConnections.any { + it.firstFrameIndex == 1 && it.secondFrameIndex == 2 + } && associationManager.associationConnections.any { + it.firstFrameIndex == 2 && it.secondFrameIndex == 0 + } + ) + } + + @Test + fun `Associates an existing frame with a non-existing frame`() { + val associationManager = AssociationManager() + associationManager.setFramesSelection(testFrames) + associationManager.associate(1, 10) + assert(associationManager.associationConnections.isEmpty()) + } + + @Test + fun `Associates frames a few times`() { + val associationManager = AssociationManager() + associationManager.setFramesSelection(testFrames) + associationManager.associate(0, 1) + associationManager.associate(0, 1) + assertEquals(1, associationManager.associationConnections.count()) + associationManager.associate(1, 0) + assertEquals(1, associationManager.associationConnections.count()) + } + + @Test + fun `Associates frames and clears association for the first frame`() { + val associationManager = AssociationManager() + associationManager.setFramesSelection(testFrames) + associationManager.associate(0, 1) + assertEquals(1, associationManager.associationConnections.count()) + associationManager.clearAssociation(0) + assertEquals(0, associationManager.associationConnections.count()) + } + + @Test + fun `Associates frames and clears association for the second frame`() { + val associationManager = AssociationManager() + associationManager.setFramesSelection(testFrames) + associationManager.associate(0, 1) + assertEquals(1, associationManager.associationConnections.count()) + associationManager.clearAssociation(1) + assertEquals(0, associationManager.associationConnections.count()) + } + + @Test + fun `Associates many-to-one frames and clears associations`() { + val associationManager = AssociationManager() + associationManager.setFramesSelection(testFrames) + associationManager.associate(1, 0) + associationManager.associate(2, 0) + associationManager.associate(3, 0) + associationManager.associate(4, 0) + assertEquals(4, associationManager.associationConnections.count()) + associationManager.clearAssociation(0) + assertEquals(0, associationManager.associationConnections.count()) + } + + companion object { + val testPointLayers = listOf( + Layer.PointLayer( + "0", + LayerSettings.PointLayerSettings("0", "0", ColorManager()) + ) { emptyList() }, + Layer.PointLayer( + "1", + LayerSettings.PointLayerSettings("1", "1", ColorManager()) + ) { emptyList() }, + Layer.PointLayer( + "2", + LayerSettings.PointLayerSettings("2", "2", ColorManager()) + ) { emptyList() }, + Layer.PointLayer( + "3", + LayerSettings.PointLayerSettings("3", "3", ColorManager()) + ) { emptyList() }, + Layer.PointLayer( + "4", + LayerSettings.PointLayerSettings("4", "4", ColorManager()) + ) { emptyList() } + ) + val testFrames = listOf( + VisualizationFrame(0L, Path("0.png"), listOf(testPointLayers[0])), + VisualizationFrame(1L, Path("1.png"), listOf(testPointLayers[1])), + VisualizationFrame(2L, Path("1.png"), listOf(testPointLayers[2])), + VisualizationFrame(3L, Path("1.png"), listOf(testPointLayers[3])), + VisualizationFrame(4L, Path("1.png"), listOf(testPointLayers[4])) + ) + } +}