Skip to content

Commit d7668de

Browse files
ancavarnemakinSurfaceYellowDuck
authored
Implement user interface in application for drawing tree. (#22)
* ci: build on multiple os, remove redundant cache * refactor: better build.gradle.kts code style * ci: Add dependabot * fix: remove debugging tools * fix: change color of RBT node to enum type * fix: Make methods for rotation abstract, fix flaky AVL test with shuffling values and other minor fixes... * implemented saving data to sqlite db * feat: implemented reading tree object from sqlite database * feat:add docker compose for lift postgresql db and small fixes * fix: Rework tree deserialization logic * add bat, sh files to up docker container * add .gitignore * fix: rework neo4j interaction * fix: null safety * fix:remove the db files in folders * fix warning associated with sf4j * feat: Initialize dependencies for UI * fix readme and add setName method to all trees * fix:fix readme * feat: add the functionality of switching between views for different trees * feat: Add the display of the binary search tree, add the ability to insert a node and clear the tree. * feat: add mapping trees list from database * Add display of the tree to choose from the list * feat: Add deletion of BST from DB and list of trees by click, little trees refactoring * add ability to save bst in db with gui * feat: add ability to delete node * fix: fix a bug related to the fact that tables were not created when starting the application * feat: Add the ability to draw an AVl tree and work with it through the graphical interface * feat: Add RBTree visualization with ability to interact with Neo4j DB * feat: better text positioning * fix: fix build on mac * feat: add tooltip to show value * feat: add tree resizing when resizing app's window * refactor * fix: setup java 11 before building * fix: review code * fix: removed space in app/build.gradle.kts * add example gif for readme * fix:add link to gif in readme * chore: add comments to bst structure * chore: Add comments for DB controllers * fix: fix linter errors * fix: fix linter errors * chore: Add more comments to DB related stuff * feat: Add ability to draw colored nodes in RBTree * chore: add comments for app * fix: fix linter errors * fix: fix linter error * fix: Remove bug when error doesn't appear after inserting empty value twice * feat: add panning * feat: restrict resizing of window's app * fix: remove scrollbar * fix: remove unnecessary border * add json folder --------- Co-authored-by: Nikita Nemakin <[email protected]> Co-authored-by: SurfaceYellowDuck <[email protected]>
1 parent dcc1a30 commit d7668de

37 files changed

+1915
-39
lines changed

.github/workflows/test.yml

+7-1
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,15 @@ jobs:
99

1010
strategy:
1111
matrix:
12-
os: [ macos-latest, ubuntu-latest, windows-latest ]
12+
os: [ ubuntu-latest, windows-latest, macos-latest ]
1313
steps:
1414
- uses: actions/checkout@v3
15+
16+
- uses: actions/setup-java@v3
17+
with:
18+
distribution: temurin
19+
java-version: 11
20+
1521
- name: Setup Gradle
1622
uses: gradle/gradle-build-action@v2
1723

README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,7 @@ And you can save red-black tree to Neo4j database
7171
val controller = Neoj4Conroller()
7272
contoller.saveTree(tree)
7373
val remTree = controller.loadTree("test")
74-
```
74+
```
75+
76+
An example of interacting with trees through a graphical interface
77+
![Example gif](https://github.com/spbu-coding-2022/trees-3/blob/add-ui/readme_gif/example1.gif)

app/build.gradle.kts

+7-14
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,25 @@
11
plugins {
2-
// Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin.
32
id("org.jetbrains.kotlin.jvm") version "1.8.10"
4-
5-
// Apply the application plugin to add support for building a CLI application in Java.
3+
id("org.openjfx.javafxplugin") version "0.0.12"
64
application
75
}
86

7+
javafx {
8+
version = "18"
9+
modules("javafx.controls", "javafx.fxml")
10+
}
11+
912
repositories {
1013
// Use Maven Central for resolving dependencies.
1114
mavenCentral()
1215
}
1316

1417
dependencies {
15-
// Use the Kotlin JUnit 5 integration.
16-
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
17-
18-
// Use the JUnit 5 integration.
19-
testImplementation("org.junit.jupiter:junit-jupiter-engine:5.9.2")
20-
18+
implementation("no.tornado:tornadofx:1.7.20")
2119
implementation(project(":trees"))
2220
}
2321

2422
application {
2523
// Define the main class for the application.
2624
mainClass.set("app.AppKt")
2725
}
28-
29-
tasks.named<Test>("test") {
30-
// Use JUnit Platform for unit tests.
31-
useJUnitPlatform()
32-
}

app/src/main/kotlin/app/App.kt

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package app
2+
3+
import app.view.MainView
4+
import tornadofx.App
5+
import tornadofx.launch
6+
7+
/**
8+
* Represents the main application class.
9+
*/
10+
11+
class MyApp : App(MainView::class)
12+
13+
/**
14+
* The main entry point of the application.
15+
*/
16+
17+
fun main() {
18+
launch<MyApp>()
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
package app.controller
2+
3+
import bst.AVLTree
4+
import bst.db.controllers.JsonController
5+
import bst.nodes.AVLNode
6+
import javafx.collections.FXCollections
7+
import javafx.collections.ObservableList
8+
import javafx.scene.control.Tooltip
9+
import javafx.scene.layout.Pane
10+
import javafx.scene.layout.StackPane
11+
import javafx.scene.paint.Color
12+
import javafx.scene.shape.Circle
13+
import javafx.scene.shape.Line
14+
import javafx.scene.text.Text
15+
import javafx.scene.text.TextBoundsType
16+
import tornadofx.Controller
17+
import kotlin.math.min
18+
19+
/**
20+
* The AVLController class is responsible for managing AVL trees.
21+
*
22+
* It extends the Controller class and provides additional functionality for working with AVL trees.
23+
*/
24+
25+
class AVLController : Controller() {
26+
27+
/**
28+
* Returns a Boolean value indicating whether the given String can be converted to an Integer.
29+
*
30+
* @param s the String to be checked
31+
* @return true if the String can be converted to an Integer, false otherwise
32+
*/
33+
34+
fun isNumeric(s: String): Boolean {
35+
return try {
36+
s.toInt()
37+
true
38+
} catch (e: NumberFormatException) {
39+
false
40+
}
41+
}
42+
43+
/**
44+
* Inserts a new node with the given key and value into the AVL Tree and redraws the tree in the tree pane.
45+
*
46+
* @param tree the AVL Tree to insert the node into
47+
* @param treePane the Pane where the AVL Tree is displayed
48+
* @param key the key of the new node to be inserted
49+
* @param value the value of the new node to be inserted
50+
*/
51+
52+
fun insertNode(tree: AVLTree<Int, String>, treePane: Pane, key: Int, value: String) {
53+
tree.insert(key, value)
54+
drawTree(tree, treePane)
55+
}
56+
57+
/**
58+
* Clears all nodes from the AVL Tree and removes all nodes from the tree pane.
59+
*
60+
* @param tree the AVL Tree to be cleared
61+
* @param treePane the Pane where the AVL Tree is displayed
62+
*/
63+
64+
fun clearTree(tree: AVLTree<Int, String>, treePane: Pane) {
65+
tree.clear()
66+
treePane.children.clear()
67+
}
68+
69+
/**
70+
* Draws the AVL Tree in the tree pane.
71+
*
72+
* @param tree the AVL Tree to be drawn
73+
* @param treePane the Pane where the AVL Tree is displayed
74+
*/
75+
76+
fun drawTree(tree: AVLTree<Int, String>, treePane: Pane) {
77+
treePane.children.clear()
78+
val root = tree.getRoot()
79+
if (root != null) {
80+
drawNode(root, treePane, treePane.width / 2.0, treePane.width / 2.0, 250.0)
81+
}
82+
}
83+
84+
/**
85+
* Draws the given [AVLNode] in the [treePane] at the specified [x] and [y] coordinates with the given [offsetX].
86+
* @param node the [AVLNode] to be drawn
87+
* @param treePane the [Pane] in which the [AVLNode] is to be drawn
88+
* @param x the x-coordinate of the [AVLNode] in the [treePane]
89+
* @param y the y-coordinate of the [AVLNode] in the [treePane]
90+
* @param offsetX the horizontal offset from the [x] coordinate at which the [AVLNode] should be drawn
91+
*/
92+
93+
private fun drawNode(node: AVLNode<Int, String>, treePane: Pane, x: Double, y: Double, offsetX: Double) {
94+
val circleRadius = 20.0
95+
val circle = Circle(circleRadius)
96+
circle.fill = Color.WHITE
97+
circle.stroke = Color.BLACK
98+
99+
val nodeText = Text(node.key.toString())
100+
nodeText.boundsType = TextBoundsType.VISUAL
101+
102+
val scale =
103+
min(circleRadius * 1.3 / nodeText.boundsInLocal.width, circleRadius * 1.3 / nodeText.boundsInLocal.height)
104+
nodeText.scaleX = scale
105+
nodeText.scaleY = scale
106+
107+
val nodeStackPane = StackPane(circle, nodeText)
108+
nodeStackPane.relocate(x - circleRadius, y - circleRadius)
109+
110+
val tooltip = Tooltip("value: ${node.value}")
111+
Tooltip.install(nodeStackPane, tooltip)
112+
113+
treePane.children.add(nodeStackPane)
114+
115+
if (node.left != null) {
116+
val leftX = x - offsetX
117+
val leftY = y + 50
118+
val leftLine = Line(x, y + circleRadius, leftX, leftY - circleRadius)
119+
treePane.children.add(leftLine)
120+
drawNode(node.left!!, treePane, leftX, leftY, offsetX / 2.0)
121+
}
122+
123+
if (node.right != null) {
124+
val rightX = x + offsetX
125+
val rightY = y + 50
126+
val rightLine = Line(x, y + circleRadius, rightX, rightY - circleRadius)
127+
treePane.children.add(rightLine)
128+
drawNode(node.right!!, treePane, rightX, rightY, offsetX / 2.0)
129+
}
130+
}
131+
132+
/**
133+
* Retrieves a list of all tree names from the JSON controller.
134+
*
135+
* @return An observable list of tree names.
136+
*/
137+
138+
fun getTreesList(): ObservableList<String>? {
139+
val controller = JsonController()
140+
val treeNames = controller.getAllTrees()
141+
val values = FXCollections.observableArrayList<String>()
142+
treeNames.forEach {
143+
values.add(it)
144+
}
145+
return values
146+
}
147+
148+
/**
149+
* Retrieves an AVL tree with the given name from the JSON controller.
150+
*
151+
* @param name The name of the tree to retrieve.
152+
* @return The AVL tree with the given name, or null if it doesn't exist.
153+
*/
154+
155+
fun getTreeFromJson(name: String): AVLTree<Int, String>? {
156+
val controller = JsonController()
157+
return controller.getTree(name)
158+
}
159+
160+
/**
161+
* Removes the AVL tree with the given name from the JSON controller.
162+
*
163+
* @param name The name of the tree to remove.
164+
*/
165+
166+
fun deleteTreeFromDB(name: String) {
167+
JsonController().run {
168+
removeTree(name)
169+
}
170+
}
171+
172+
/**
173+
* Saves the given AVL tree to the JSON controller with the given name.
174+
*
175+
* @param tree The AVL tree to save.
176+
* @param treeName The name to use when saving the tree.
177+
*/
178+
179+
fun saveTree(tree: AVLTree<Int, String>, treeName: String) {
180+
val controller = JsonController()
181+
controller.saveTree(tree, treeName)
182+
}
183+
184+
/**
185+
* Removes a node with the given value from the given AVL tree and redraws the tree on the given pane.
186+
*
187+
* @param value The value of the node to remove.
188+
* @param tree The AVL tree to remove the node from.
189+
* @param treePane The pane where the tree is to be drawn.
190+
*/
191+
192+
fun deleteNode(value: Int, tree: AVLTree<Int, String>, treePane: Pane) {
193+
tree.remove(value)
194+
drawTree(tree, treePane)
195+
}
196+
}

0 commit comments

Comments
 (0)