Skip to content

Commit

Permalink
feat: Add the ability to draw an AVl tree and work with it through th…
Browse files Browse the repository at this point in the history
…e graphical interface
  • Loading branch information
SurfaceYellowDuck committed May 1, 2023
1 parent 360080d commit 1fa30f7
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 9 deletions.
101 changes: 101 additions & 0 deletions app/src/main/kotlin/app/controller/AVLController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package app.controller

import bst.AVLTree
import bst.db.controllers.JsonController
import bst.nodes.AVLNode
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 AVLController: Controller() {
fun isNumeric(s: String): Boolean {
return try {
s.toInt()
true
} catch (e: NumberFormatException) {
false
}
}

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

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

//make here not null check
fun drawTree(tree: AVLTree<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: AVLNode<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 = Color.WHITE
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 = JsonController()
val treeNames = controller.getAllTrees()
val values = FXCollections.observableArrayList<String>()
treeNames.forEach {
values.add(it)
}
return values
}
fun getTreeFromJson(name: String): AVLTree<Int, String>? {
val controller = JsonController()
val tree = controller.getTree(name)
return tree
}

fun deleteTreeFromDB(name: String) {
JsonController().run {
removeTree(name)
}
}
fun saveTree(tree: AVLTree<Int, String>, treeName: String) {
val controller = JsonController()
controller.saveTree(tree, treeName)
}
fun deleteNode(value: Int, tree: AVLTree<Int, String>, treePane: Pane){
tree.remove(value)
drawTree(tree, treePane)
}
}
2 changes: 0 additions & 2 deletions app/src/main/kotlin/app/controller/BSTController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import javafx.scene.control.Label
import bst.db.controllers.SQLController
import javafx.collections.FXCollections.observableArrayList
import javafx.collections.ObservableList
import kotlin.reflect.jvm.internal.impl.resolve.constants.KClassValue.Value

class BSTController : Controller() {
fun isNumeric(s: String): Boolean {
Expand All @@ -30,7 +29,6 @@ class BSTController : Controller() {
}

fun clearTree(tree: BSTree<Int, String>, treePane: Pane) {
val controller = SQLController()
tree.clear()
treePane.children.clear()
}
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/kotlin/app/controller/RBTController.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package app.controller

class RBTController {

}
111 changes: 111 additions & 0 deletions app/src/main/kotlin/app/view/treeView/AVLTreeView.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,112 @@
package app.view.treeView
import app.controller.AVLController
import bst.AVLTree
import bst.BSTree
import javafx.beans.property.SimpleStringProperty
import javafx.scene.control.Alert
import javafx.scene.layout.Pane
import tornadofx.*
class AVLTreeView: View() {
private val controller: AVLController by inject()
private var tree = AVLTree<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()
override val root = vbox {
hbox {
val availableTrees = combobox<String> {
this@AVLTreeView.trees?.let { items.addAll(it) }
selectionModel.selectedItemProperty().addListener { _, _, newValue ->
this@AVLTreeView.selectedItem = newValue
}
}

button("Select") {
action {
println("Selected item: $selectedItem")
val loadedTree = selectedItem?.let { controller.getTreeFromJson(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("Binary Search Tree") {
action {
replaceWith(BinarySearchTreeView::class, ViewTransition.Slide(0.3.seconds, ViewTransition.Direction.LEFT))
Expand All @@ -12,5 +117,11 @@ class AVLTreeView: View() {
replaceWith(RedBlackTreeView::class, ViewTransition.Slide(0.3.seconds, ViewTransition.Direction.LEFT))
}
}
this += treePane
treePane.apply {
minWidth = 600.0
minHeight = 400.0
style = "-fx-border-color: black;"
}
}
}
7 changes: 4 additions & 3 deletions trees/src/main/kotlin/bst/AbstractBST.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import bst.nodes.BinaryNode

abstract class AbstractBST<K : Comparable<K>, V, Self : BinaryNode<K, V, Self>> : Tree<K, V> {

var treeName: String = ""
internal var rootNode: Self? = null

// fun setName(treeName: String){
// this.treeName = treeName
// }
fun setName(treeName: String){
this.treeName = treeName
}

fun getRoot(): Self? = this.rootNode

Expand Down
19 changes: 15 additions & 4 deletions trees/src/main/kotlin/bst/db/controllers/JsonController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import java.io.FileReader
import java.io.FileWriter

class JsonController : Controller<AVLNode<Int, String>, AVLTree<Int, String>> {
val folderPath = "json"
override fun saveTree(tree: AVLTree<Int, String>, treeName: String) {
val gson = Gson()
try {
val writer = FileWriter("$treeName.json")
val writer = FileWriter("${this.folderPath}/$treeName.json")
gson.toJson(tree, writer)
writer.close()
} catch (e: Exception) {
Expand All @@ -22,7 +23,7 @@ class JsonController : Controller<AVLNode<Int, String>, AVLTree<Int, String>> {
override fun getTree(treeName: String): AVLTree<Int, String>? {
val gson = Gson()
return try {
val reader = FileReader("$treeName.json")
val reader = FileReader("${this.folderPath}/$treeName.json")
val tree = gson.fromJson(reader, AVLTree<Int, String>()::class.java)
reader.close()
tree
Expand All @@ -33,6 +34,16 @@ class JsonController : Controller<AVLNode<Int, String>, AVLTree<Int, String>> {
}

override fun removeTree(treeName: String) {
File("$treeName.json").delete()
File("${this.folderPath}/$treeName.json").delete()
}
}

fun getAllTrees(): List<String>{
val jsonFiles = File(folderPath).listFiles{ file -> file.extension == "json" }
val jsonNames = mutableListOf<String>()
jsonFiles?.forEach { file ->
jsonNames.add(file.name.removeSuffix(".json"))
// jsonNames.add(file.name)
}
return jsonNames
}
}

0 comments on commit 1fa30f7

Please sign in to comment.