Skip to content

Commit

Permalink
add association ui
Browse files Browse the repository at this point in the history
  • Loading branch information
mi-sts committed May 4, 2024
1 parent 1e1fcb6 commit 891cf8a
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 36 deletions.
104 changes: 95 additions & 9 deletions src/main/kotlin/solve/rendering/canvas/SceneCanvas.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@ import solve.scene.model.Landmark
import solve.scene.model.Layer
import solve.scene.model.Scene
import solve.scene.model.VisualizationFrame
import solve.scene.view.association.AssociationAdorner
import solve.scene.view.association.AssociationManager
import solve.utils.ServiceLocator
import solve.utils.action
import solve.utils.ceilToInt
import solve.utils.getScreenPosition
import solve.utils.item
import solve.utils.lineSeparator
import solve.utils.removeSafely
import solve.utils.structures.DoublePoint
import tornadofx.*
import solve.rendering.engine.scene.Scene as EngineScene

class SceneCanvas : OpenGLCanvas() {
Expand Down Expand Up @@ -60,8 +64,13 @@ class SceneCanvas : OpenGLCanvas() {
private var isFirstFramesSelection = false

private var contextMenuInvokeFrame: VisualizationFrame? = null
private var contextMenuInvokeFrameIndex = 0

private val associationManager = AssociationManager()
private var firstAssociatingFrameIndex = 0
private var isFirstAssociatingFrameChosen = false
private var associationAdorner: AssociationAdorner? = null


private val contextMenu = buildContextMenu(canvas)

Expand Down Expand Up @@ -90,10 +99,7 @@ class SceneCanvas : OpenGLCanvas() {
engineScene?.setFramesSelection(framesSelection)
associationManager.setFramesSelection(framesSelection)
framesSelectionSize = framesSelection.count()
lastFramesSelection = framesSelection

associationManager.associate(0, 12)
}
lastFramesSelection = framesSelection }

fun setColumnsNumber(columnsNumber: Int) {
this.columnsNumber = columnsNumber
Expand Down Expand Up @@ -132,9 +138,15 @@ class SceneCanvas : OpenGLCanvas() {
Vector2f(frameLocalCoordinate.x * framesSize.x, frameLocalCoordinate.y * framesSize.y).toIntVector()

if (mouseButton == landmarkInteractionMouseButton) {
if (isFirstAssociatingFrameChosen) {
onSecondAssociatingFrameChosen(frameIndex)
return
}

tryInteractWithLandmark(frameIndex, framePixelCoordinate)
} else if (mouseButton == contextMenuMouseButton) {
contextMenuInvokeFrame = getContextMenuInvokeFrame(frameIndex) ?: return
contextMenuInvokeFrameIndex = frameIndex
val canvasScreenPosition = canvas.getScreenPosition()
contextMenu.show(
canvas,
Expand Down Expand Up @@ -311,11 +323,55 @@ class SceneCanvas : OpenGLCanvas() {

private fun MFXContextMenu.addAssociateKeypointsItem() {
item("Associate keypoints").action {
val invokeFrame = contextMenuInvokeFrame ?: return@action
onFirstAssociatingFrameChosen(contextMenuInvokeFrameIndex)
}
}

println("assoiation")
invokeFrame.toString()
private fun onFirstAssociatingFrameChosen(firstFrameIndex: Int) {
firstAssociatingFrameIndex = contextMenuInvokeFrameIndex
isFirstAssociatingFrameChosen = true
createAssociationAdorner(firstFrameIndex)
}

private fun onSecondAssociatingFrameChosen(secondFrameIndex: Int) {
if (!isFirstAssociatingFrameChosen)
return

if (firstAssociatingFrameIndex != secondFrameIndex) {
associationManager.associate(firstAssociatingFrameIndex, secondFrameIndex)
}

isFirstAssociatingFrameChosen = false
val removingAssociationAdorner = associationAdorner ?: return
canvas.removeSafely(removingAssociationAdorner.node)
removingAssociationAdorner.destroy()
}

private fun createAssociationAdorner(firstFrameIndex: Int) {
val getFrameScreenPosition = {
val frameIndexCoordinates = Vector2i(firstFrameIndex % columnsNumber, firstFrameIndex / columnsNumber)
val frameTopLeftRelativeScreenPosition = getFrameTopLeftCornerRelativeScreenPosition(
frameIndexCoordinates,
framesSize,
window
)

DoublePoint(
frameTopLeftRelativeScreenPosition.x.toDouble(),
frameTopLeftRelativeScreenPosition.y.toDouble()
)
}
val getScreenScale = {
window.camera.zoom.toDouble() / IdentityFramesSizeScale
}
val associationAdorner = AssociationAdorner(
framesSize.x.toDouble(),
framesSize.y.toDouble(),
getFrameScreenPosition,
getScreenScale
)
canvas.add(associationAdorner.node)
this.associationAdorner = associationAdorner
}

private fun MFXContextMenu.addClearAssociationsItem() {
Expand All @@ -337,7 +393,7 @@ class SceneCanvas : OpenGLCanvas() {
}

companion object {
const val IdentityFramesSizeScale = 1.605f
const val IdentityFramesSizeScale = 1.6f

private val landmarkInteractionMouseButton = MouseButton.Left
private val contextMenuMouseButton = MouseButton.Right
Expand All @@ -350,7 +406,7 @@ class SceneCanvas : OpenGLCanvas() {
)
}

// Converts screen vector to frame coordinates.
// Converts a screen vector to frame coordinates.
// One frame including spacing area corresponds to a (1, 1) frame vector.
fun screenToFrameVector(screenVector: Vector2i, framesSize: Vector2i, window: Window): Vector2f {
val shaderVector = window.screenToShaderVector(screenVector)
Expand All @@ -376,5 +432,35 @@ class SceneCanvas : OpenGLCanvas() {

return shaderVector
}


// Returns a frame position in screen coordinates without taking into account the camera position.
fun getGlobalFrameScreenPosition(frameIndexCoordinates: Vector2i, framesSize: Vector2i) : Vector2f {
return Vector2f(
(framesSize.x.toFloat() + Renderer.getSpacingWidth(framesSize)) * frameIndexCoordinates.x ,
(framesSize.y.toFloat() * (1f + Renderer.FramesSpacing)) * frameIndexCoordinates.y
)
}

// Returns a top left corner position in screen coordinates without taking into account the camera position.
fun getGlobalTopLeftCornerScreenPosition(window: Window, framesSize: Vector2i) : Vector2f {
val topLeftCornerShaderPosition = window.calculateTopLeftCornerShaderPosition()
return shaderToFrameVector(topLeftCornerShaderPosition, framesSize).also {
it.x *= framesSize.x
it.y *= framesSize.y
}
}

// Returns a frame position in screen coordinates with taking into account the camera position.
fun getFrameTopLeftCornerRelativeScreenPosition(
frameIndexCoordinates: Vector2i,
framesSize: Vector2i,
window: Window
) : Vector2f {
val globalFrameScreenPosition = getGlobalFrameScreenPosition(frameIndexCoordinates, framesSize)
val globalTopLeftCornerScreenPosition = getGlobalTopLeftCornerScreenPosition(window, framesSize)

return globalFrameScreenPosition - globalTopLeftCornerScreenPosition
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,6 @@ class PointAssociationsRenderer(
associationLine.secondKeypoint.coordinate.toVector2i().toFloatVector()
)

println("line start shader pos: ${lineStartShaderPosition}")
println("line finish shader pos: ${lineFinishShaderPosition}")

val lineVector = lineFinishShaderPosition - lineStartShaderPosition
val normalVector = Vector2f(-lineVector.y, lineVector.x).normalize()
val linePoints = listOf(lineStartShaderPosition, lineFinishShaderPosition)
Expand Down
52 changes: 28 additions & 24 deletions src/main/kotlin/solve/scene/view/association/AssociationAdorner.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package solve.scene.view.association

import javafx.beans.InvalidationListener
import javafx.beans.property.DoubleProperty
import javafx.animation.AnimationTimer
import javafx.scene.Node
import javafx.scene.control.Label
import javafx.scene.layout.StackPane
Expand All @@ -20,36 +19,39 @@ import tornadofx.*
class AssociationAdorner(
private val width: Double,
private val height: Double,
private val framePosition: DoublePoint,
private val scale: DoubleProperty
) {
private val getFrameScreenPosition: () -> DoublePoint,
private val getScale: () -> Double
) : AnimationTimer() {
private val pane = StackPane()

private val scale: Double
get() = getScale()

private val frameScreenPosition: DoublePoint
get() = getFrameScreenPosition()

init {
start()
}

/**
* Adorner visual node.
*/
val node: Node = pane

private lateinit var rectangle: Rectangle
private val rectangle = Rectangle()

private val scaleChangedListener = InvalidationListener {
pane.prefWidth = width * scale.value
pane.prefHeight = height * scale.value
pane.layoutX = framePosition.x * scale.value
pane.layoutY = framePosition.y * scale.value
rectangle.width = width * scale.value
rectangle.height = height * scale.value
private fun updateView() {
pane.prefWidth = width * scale
pane.prefHeight = height * scale
pane.layoutX = frameScreenPosition.x * scale
pane.layoutY = frameScreenPosition.y * scale
rectangle.width = width * scale
rectangle.height = height * scale
}

init {
pane.prefWidth = width * scale.value
pane.prefHeight = height * scale.value
pane.layoutX = framePosition.x * scale.value
pane.layoutY = framePosition.y * scale.value

rectangle = Rectangle()
rectangle.width = width * scale.value
rectangle.height = height * scale.value
updateView()
rectangle.fill = Paint.valueOf(Style.primaryColor)
rectangle.opacity = RectangleOpacity

Expand All @@ -60,12 +62,14 @@ class AssociationAdorner(

pane.add(rectangle)
pane.add(label)
}

scale.addListener(scaleChangedListener)
override fun handle(currentTime: Long) {
updateView()
}

fun dispose() {
scale.removeListener(scaleChangedListener)
fun destroy() {
stop()
}

companion object {
Expand Down

0 comments on commit 891cf8a

Please sign in to comment.