Skip to content

Commit

Permalink
Merge pull request #46 from spbu-coding-2023/elbanan-retake-work
Browse files Browse the repository at this point in the history
YifanHu placement
  • Loading branch information
ElBananium authored Sep 24, 2024
2 parents b8586cc + a5b7f7e commit 4b14316
Show file tree
Hide file tree
Showing 17 changed files with 220 additions and 14 deletions.
3 changes: 3 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions .idea/gradle.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/kotlinc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ repositories {
}

dependencies {
implementation(files("libs/gephi-toolkit-0.10.0-all.jar"))
testImplementation(kotlin("test"))
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
implementation(compose.desktop.currentOs)
Expand Down
Binary file added libs/gephi-toolkit-0.10.0-all.jar
Binary file not shown.
22 changes: 15 additions & 7 deletions src/main/kotlin/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,34 @@ import model.graphs.graphs.WeightedGraph
import view.screens.graphChooseScreen
import view.screens.mainScreens.mainScreen
import view.screens.mainScreens.mainScreenWeightedGraph
import viewModel.graphViewModel.CircularPlacementStrategy
import view.screens.placementStrategyScreen
import viewModel.screensViewModels.GraphChooseViewModel
import viewModel.screensViewModels.PlacementStrategyViewModel
import viewModel.screensViewModels.mainScreensViewModels.MainScreenViewModel
import viewModel.screensViewModels.mainScreensViewModels.MainScreenViewModelWeightedGraph

var closeFirstWindow: MutableState<Boolean> = mutableStateOf(false)
var closePlacementStrategyWindow: MutableState<Boolean> = mutableStateOf(false)
val graphChooseVM = GraphChooseViewModel<String>()
val placementStrategyVM = PlacementStrategyViewModel()

@Composable
@Preview
fun app() {
MaterialTheme {
closeFirstWindow = remember { mutableStateOf(false) }
closePlacementStrategyWindow = remember { mutableStateOf(false) }
if (!closePlacementStrategyWindow.value) {
placementStrategyScreen(placementStrategyVM) {
closePlacementStrategyWindow.value = true
}
}
if (!closeFirstWindow.value) {
graphChooseScreen(graphChooseVM) {
closeFirstWindow.value = true
}
}
if (closeFirstWindow.value) {
if (closeFirstWindow.value && closePlacementStrategyWindow.value) {
if (!graphChooseVM.isDirected && !graphChooseVM.isWeighted) {
val sampleGraph = Graph<String>().apply {
addVertex("A")
Expand All @@ -42,7 +51,6 @@ fun app() {
addVertex("E")
addVertex("F")
addVertex("G")

addEdge(1, 0)
addEdge(0, 2)
addEdge(0, 3)
Expand All @@ -53,7 +61,7 @@ fun app() {
addEdge(5, 6)
addEdge(6, 4)
}
mainScreen(MainScreenViewModel(sampleGraph, CircularPlacementStrategy()), false)
mainScreen(MainScreenViewModel(sampleGraph, placementStrategyVM.placementStrategy), false)
} else if (graphChooseVM.isDirected && !graphChooseVM.isWeighted) {
val sampleGraph = DirectedGraph<String>().apply {
addVertex("A")
Expand All @@ -74,7 +82,7 @@ fun app() {
addEdge(5, 6)
addEdge(6, 4)
}
mainScreen(MainScreenViewModel(sampleGraph, CircularPlacementStrategy()), true)
mainScreen(MainScreenViewModel(sampleGraph, placementStrategyVM.placementStrategy), true)
} else if (!graphChooseVM.isDirected) {
val sampleGraph = WeightedGraph<String>().apply {
addVertex("A")
Expand Down Expand Up @@ -106,7 +114,7 @@ fun app() {
changeEdgeWeight(8, 5)
}
mainScreenWeightedGraph(
MainScreenViewModelWeightedGraph(sampleGraph, CircularPlacementStrategy()),
MainScreenViewModelWeightedGraph(sampleGraph, placementStrategyVM.placementStrategy),
false
)
} else {
Expand Down Expand Up @@ -140,7 +148,7 @@ fun app() {
changeEdgeWeight(8, 5)
}
mainScreenWeightedGraph(
MainScreenViewModelWeightedGraph(sampleGraph, CircularPlacementStrategy()),
MainScreenViewModelWeightedGraph(sampleGraph, placementStrategyVM.placementStrategy),
true
)
}
Expand Down
63 changes: 63 additions & 0 deletions src/main/kotlin/model/algorithms/placement/YifanHuPlacement.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package model.algorithms.placement

import model.graphs.edges.Edge
import model.graphs.graphs.AbstractGraph
import org.gephi.graph.api.GraphModel
import org.gephi.graph.api.Node
import org.gephi.layout.plugin.force.StepDisplacement
import org.gephi.layout.plugin.force.yifanHu.YifanHuLayout
import kotlin.random.Random

open class YifanHuPlacement<V, E : Edge>(val graph: AbstractGraph<V, E>) {
fun getPlacement(width: Int, height: Int): Map<V, Pair<Float, Float>> {
val random = Random(1L)

val graphModel = GraphModel.Factory.newInstance()
val graphType = graphModel.undirectedGraph

val nodes = mutableMapOf<V, Node>()
for (vertex in graph.vertices.values) {
val node: Node = graphModel.factory().newNode(vertex.value.toString())
node.setX(random.nextFloat() * width)
node.setY(random.nextFloat() * height)
nodes[vertex.value] = node

graphType.addNode(node)
}

for (edge in graph.edges.values) {
val firstVertex = graph.getVertexValue(edge.verticesNumbers.first)
val secondVertex = graph.getVertexValue(edge.verticesNumbers.second)
val e: org.gephi.graph.api.Edge = graphModel.factory().newEdge(
nodes[firstVertex],
nodes[secondVertex],
1,
false
)
graphType.addEdge(e)
}
val layout = YifanHuLayout(null, StepDisplacement(1f))
layout.setGraphModel(graphModel)
layout.initAlgo()
layout.resetPropertiesValues()
layout.optimalDistance = 300f
layout.relativeStrength = 0.2f
layout.initialStep = 20.0f
layout.stepRatio = 0.95f
layout.isAdaptiveCooling = true

var i = 0
while (i < 1000 && layout.canAlgo()) {
layout.goAlgo()
i++
}
layout.endAlgo()
val placement = mutableMapOf<V, Pair<Float, Float>>()
for (vertex in graph.vertices.values) {
val n = graphType.getNode(vertex.value.toString())
placement[vertex.value] = Pair(n.x(), n.y())
}

return placement
}
}
40 changes: 40 additions & 0 deletions src/main/kotlin/view/screens/PlacementStrategyScreen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package view.screens

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.unit.dp
import viewModel.screensViewModels.PlacementStrategyViewModel

@Composable
fun placementStrategyScreen(viewModel: PlacementStrategyViewModel, onDismiss: () -> Unit) {
Row(
horizontalArrangement = Arrangement.spacedBy(20.dp)
) {
Button(
onClick = {
viewModel.selectYifanHu()
onDismiss()
},
enabled = true
) {
Text(
text = "YifanHu"
)
}

Button(
onClick = {
viewModel.selectCircular()
onDismiss()
},
enabled = true
) {
Text(
text = "Circular"
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package viewModel.graphViewModel

import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import model.graphs.edges.Edge
import model.graphs.graphs.AbstractGraph
import viewModel.graphViewModel.edgesViewModel.EdgeViewModel
import kotlin.math.cos
import kotlin.math.min
import kotlin.math.sin

class CircularPlacementStrategy : RepresentationStrategy {
override fun <V> place(width: Double, height: Double, vertices: Collection<VertexViewModel<V>>) {
override fun <V, E : Edge> place(width: Double, height: Double, graph: AbstractGraph<V, E>, vertices: Collection<VertexViewModel<V>>) {
if (vertices.isEmpty()) {
println("CircularPlacementStrategy.place: there is nothing to place 👐🏻")
return
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package viewModel.graphViewModel

import androidx.compose.ui.graphics.Color
import model.graphs.edges.Edge
import model.graphs.graphs.AbstractGraph
import viewModel.graphViewModel.edgesViewModel.EdgeViewModel

interface RepresentationStrategy {
fun <V> place(width: Double, height: Double, vertices: Collection<VertexViewModel<V>>)
fun <V, E : Edge> place(width: Double, height: Double, graph: AbstractGraph<V, E>, vertices: Collection<VertexViewModel<V>>)
fun <V> highlightVertices(vertices: Collection<VertexViewModel<V>>, color: Color)
fun <V> highlightEdges(edges: Collection<EdgeViewModel<V>>, color: Color)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class VertexViewModel<V>(
x: Dp = 0.dp,
y: Dp = 0.dp,
color: Color,
private val v: Vertex<V>,
val v: Vertex<V>,
private val _labelVisible: State<Boolean>,
val radius: Dp = 25.dp
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package viewModel.graphViewModel

import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import model.algorithms.placement.YifanHuPlacement
import model.graphs.edges.Edge
import model.graphs.graphs.AbstractGraph
import viewModel.graphViewModel.edgesViewModel.EdgeViewModel

class YifanHuPlacementStrategy : RepresentationStrategy {
override fun <V, E : Edge> place(
width: Double,
height: Double,
graph: AbstractGraph<V, E>,
vertices: Collection<VertexViewModel<V>>
) {
val placemnt = YifanHuPlacement(graph).getPlacement(width.toInt(), height.toInt())

for (vertex in vertices) {
vertex.x = placemnt[vertex.v.value]?.first?.dp ?: 0f.dp
vertex.y = placemnt[vertex.v.value]?.second?.dp ?: 0f.dp
}
}

override fun <V> highlightVertices(vertices: Collection<VertexViewModel<V>>, color: Color) {
vertices
.onEach {
it.color = color
}
}

override fun <V> highlightEdges(edges: Collection<EdgeViewModel<V>>, color: Color) {
edges
.onEach {
it.color = color
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package viewModel.screensViewModels

import viewModel.graphViewModel.CircularPlacementStrategy
import viewModel.graphViewModel.RepresentationStrategy
import viewModel.graphViewModel.YifanHuPlacementStrategy

class PlacementStrategyViewModel {
internal var placementStrategy: RepresentationStrategy = CircularPlacementStrategy()

fun selectYifanHu() {
placementStrategy = YifanHuPlacementStrategy()
}
fun selectCircular() {
placementStrategy = CircularPlacementStrategy()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ class MainScreenViewModel<V>(val graph: Graph<V>, private val representationStra
val graphViewModel = GraphViewModel(graph, showVerticesLabels)

init {
representationStrategy.place(800.0, 600.0, graphViewModel.vertices)
representationStrategy.place(800.0, 600.0, graph, graphViewModel.vertices)
}

fun resetGraphView() {
representationStrategy.place(800.0, 600.0, graphViewModel.vertices)
representationStrategy.place(800.0, 600.0, graph, graphViewModel.vertices)
graphViewModel.vertices.forEach { v -> v.color = Color.Gray }
graphViewModel.edges.forEach { e -> e.color = Color.Black }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ class MainScreenViewModelWeightedGraph<V>(
val graphViewModel = WeightedGraphViewModel(graph, showVerticesLabels, showEdgesLabels)

init {
representationStrategy.place(800.0, 600.0, graphViewModel.vertices)
representationStrategy.place(800.0, 600.0, graph, graphViewModel.vertices)
}

fun resetGraphView() {
representationStrategy.place(800.0, 600.0, graphViewModel.vertices)
representationStrategy.place(800.0, 600.0, graph, graphViewModel.vertices)
graphViewModel.vertices.forEach { v -> v.color = Color.Gray }
graphViewModel.edges.forEach { e -> e.color = Color.Black }
}
Expand Down

0 comments on commit 4b14316

Please sign in to comment.