Skip to content

Commit

Permalink
feat: Add RBTree visualization with ability to interact with Neo4j DB
Browse files Browse the repository at this point in the history
  • Loading branch information
nemakin committed May 1, 2023
1 parent 1fa30f7 commit 2163189
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 9 deletions.
98 changes: 97 additions & 1 deletion app/src/main/kotlin/app/controller/RBTController.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,101 @@
package app.controller

class RBTController {
import bst.RedBlackTree
import bst.nodes.RBTNode
import bst.db.controllers.JsonController
import bst.db.controllers.Neo4jController
import javafx.collections.FXCollections
import javafx.collections.ObservableList
import javafx.scene.control.Label
import javafx.scene.layout.Pane
import javafx.scene.paint.Color
import javafx.scene.shape.Circle
import javafx.scene.shape.Line
import tornadofx.Controller

class RBTController: Controller() {
fun isNumeric(s: String): Boolean {
return try {
s.toInt()
true
} catch (e: NumberFormatException) {
false
}
}

fun insertNode(tree: RedBlackTree<Int, String>, treePane: Pane, key: Int, value: String) {
tree.insert(key, value)
drawTree(tree, treePane)
}

fun clearTree(tree: RedBlackTree<Int, String>, treePane: Pane) {
tree.clear()
treePane.children.clear()
}

//make here not null check
fun drawTree(tree: RedBlackTree<Int, String>, treePane: Pane) {
treePane.children.clear()
val root = tree.getRoot()
if (root != null) {
drawNode(root, treePane, treePane.width / 2.0, 50.0, treePane.width / 4.0)
}
}

private fun drawNode(node: RBTNode<Int, String>, treePane: Pane, x: Double, y: Double, offsetX: Double) {
val circleRadius = 20.0
val circle = Circle(circleRadius)
circle.centerX = x
circle.centerY = y
circle.fill = if (node.color == RBTNode.Color.RED) Color.RED else Color.BLACK
circle.stroke = Color.BLACK
val nodeLabel = Label(node.key.toString())
nodeLabel.layoutX = circle.centerX - (circle.radius / 3)
nodeLabel.layoutY = circle.centerY - (circle.radius / 3)
treePane.children.addAll(circle, nodeLabel)

if (node.left != null) {
val leftX = x - offsetX
val leftY = y + 50
val leftLine = Line(x, y + circleRadius, leftX, leftY - circleRadius)
treePane.children.add(leftLine)
drawNode(node.left!!, treePane, leftX, leftY, offsetX / 2.0)
}

if (node.right != null) {
val rightX = x + offsetX
val rightY = y + 50
val rightLine = Line(x, y + circleRadius, rightX, rightY - circleRadius)
treePane.children.add(rightLine)
drawNode(node.right!!, treePane, rightX, rightY, offsetX / 2.0)
}
}

fun getTreesList(): ObservableList<String>? {
val controller = Neo4jController()
val treeNames = controller.getNames()
val values = FXCollections.observableArrayList<String>()
treeNames.forEach {
values.add(it)
}
return values
}
fun getTreeFromNeo4j(name: String): RedBlackTree<Int, String>? {
val controller = Neo4jController()
return controller.getTree(name)
}

fun deleteTreeFromDB(name: String) {
Neo4jController().run {
removeTree(name)
}
}
fun saveTree(tree: RedBlackTree<Int, String>, treeName: String) {
val controller = Neo4jController()
controller.saveTree(tree, treeName)
}
fun deleteNode(value: Int, tree: RedBlackTree<Int, String>, treePane: Pane){
tree.remove(value)
drawTree(tree, treePane)
}
}
3 changes: 2 additions & 1 deletion app/src/main/kotlin/app/view/treeView/AVLTreeView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class AVLTreeView: View() {
private var selectedItem: String? = ""
private val treeName = SimpleStringProperty()
private val valueFotDeletion = SimpleStringProperty()

override val root = vbox {
hbox {
val availableTrees = combobox<String> {
Expand Down Expand Up @@ -72,7 +73,7 @@ class AVLTreeView: View() {
value.value = ""
}
}
field("Value input"){
field("Key input"){
textfield(valueFotDeletion)
}
button("Delete node"){
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class BinarySearchTreeView : View() {
private var selectedItem: String? = ""
private val treeName = SimpleStringProperty()
private val valueFotDeletion = SimpleStringProperty()

override val root = vbox {
hbox {
val availableTrees = combobox<String> {
Expand Down Expand Up @@ -74,7 +75,7 @@ class BinarySearchTreeView : View() {
value.value = ""
}
}
field("Value input"){
field("Key input"){
textfield(valueFotDeletion)
}
button("Delete node"){
Expand Down
121 changes: 115 additions & 6 deletions app/src/main/kotlin/app/view/treeView/RedBlackTreeView.kt
Original file line number Diff line number Diff line change
@@ -1,22 +1,131 @@
package app.view.treeView
import app.view.treeView.AVLTreeView
import app.view.treeView.BinarySearchTreeView

import app.controller.RBTController
import tornadofx.*
import bst.RedBlackTree
import bst.db.controllers.Neo4jController
import javafx.beans.property.SimpleStringProperty
import javafx.scene.layout.Pane
import javafx.scene.control.Alert


class RedBlackTreeView : View() {
private val controller: RBTController by inject()
private var tree = RedBlackTree<Int, String>()
private val treePane = Pane()
private val key = SimpleStringProperty()
private val value = SimpleStringProperty()
private var trees = controller.getTreesList()
private var selectedItem: String? = ""
private val treeName = SimpleStringProperty()
private val valueFotDeletion = SimpleStringProperty()

class RedBlackTreeView: View() {
override val root = vbox {
hbox {
val availableTrees = combobox<String> {
this@RedBlackTreeView.trees?.let { items.addAll(it) }
selectionModel.selectedItemProperty().addListener { _, _, newValue ->
this@RedBlackTreeView.selectedItem = newValue
}
}

button("Select") {
action {
println("Selected item: $selectedItem")
val loadedTree = selectedItem?.let { controller.getTreeFromNeo4j(it) }
if (loadedTree != null) {
tree = loadedTree
controller.drawTree(tree, treePane)
}
}
}
button("Delete") {
action {
selectedItem?.let {
controller.clearTree(tree, treePane)
controller.deleteTreeFromDB(it)
}
availableTrees.items.remove(selectedItem)

println("Item deleted: $selectedItem")
}
}

button("Clear") {
action {
controller.clearTree(tree, treePane)
}
}
form {
fieldset {
field("Key input") {
textfield(key)
}
field("Value input") {
textfield(value)
}

button("Add Node") {
action {
if (key.value != null && value.value != null && controller.isNumeric(key.value)) {
controller.insertNode(tree, treePane, key.value.toInt(), value.value)
} else {
alert(type = Alert.AlertType.ERROR, header = "Insertion Error")
}
key.value = ""
value.value = ""
}
}
field("Value input"){
textfield(valueFotDeletion)
}
button("Delete node"){
action {
if (controller.isNumeric(valueFotDeletion.value)){
controller.deleteNode(valueFotDeletion.value.toInt(), tree, treePane)
}
else{
alert(type = Alert.AlertType.ERROR, header = "Deletion Error")
}
}
}

field("Input tree name") {
textfield(treeName)
}
button("Save tree") {
action {
if (tree.getRoot() != null) {
// tree.treeName = treeName.value
controller.saveTree(tree, treeName.value)
if (!availableTrees.items.contains(treeName.value)) {
availableTrees.items.add(treeName.value)
}
}
else{
alert(type = Alert.AlertType.ERROR, header = "Can not save tree with empty root")
}
}
}
}
}
}
button("AVL Tree") {
action {
replaceWith(AVLTreeView::class, ViewTransition.Slide(0.3.seconds, ViewTransition.Direction.LEFT))

}
}

button("Binary Search Tree") {
action {
replaceWith(BinarySearchTreeView::class, ViewTransition.Slide(0.3.seconds, ViewTransition.Direction.LEFT))

}
}
this += treePane
treePane.apply {
minWidth = 600.0
minHeight = 400.0
style = "-fx-border-color: black;"
}
}
}
}

0 comments on commit 2163189

Please sign in to comment.