Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.writeopia.forcegraph
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.BoxWithConstraintsScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
Expand All @@ -16,6 +17,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.unit.sp
import kotlin.math.min
import kotlin.math.sqrt

class Node(
Expand All @@ -42,7 +44,7 @@ data class Link(
)

@Composable
fun ForceDirectedGraph(
fun BoxWithConstraintsScope.ForceDirectedGraph(
nodes: List<Node>,
links: List<Link>,
onNodeSelected: (String) -> Unit
Expand All @@ -55,6 +57,8 @@ fun ForceDirectedGraph(
fontSize = 10.sp
)

val nodeRadius = if (nodes.size < 15) (25 - nodes.size).toFloat() else 8f

Canvas(
modifier = Modifier
.fillMaxSize()
Expand Down Expand Up @@ -104,12 +108,17 @@ fun ForceDirectedGraph(
)
}
) {
// Draw links
links.forEach { link ->
drawLine(
color = Color.LightGray,
start = Offset(link.source.x, link.source.y),
end = Offset(link.target.x, link.target.y),
start = Offset(
min(link.source.x, maxWidth.value),
min(link.source.y, maxHeight.value)
),
end = Offset(
min(link.target.x, maxWidth.value),
min(link.target.y, maxHeight.value)
),
strokeWidth = 2f
)
}
Expand All @@ -122,12 +131,11 @@ fun ForceDirectedGraph(
}
}

// Draw nodes
nodes.forEach { node ->
drawCircle(
color = if (node.isDragged) Color.Red else nodeColor(node.isFolder),
center = Offset(node.x, node.y),
radius = if (node.isFolder) 12f else 6f
center = Offset(min(node.x, maxWidth.value), min(node.y, maxHeight.value)),
radius = if (node.isFolder) nodeRadius else nodeRadius / 2
)
}

Expand All @@ -136,7 +144,7 @@ fun ForceDirectedGraph(
val x = node.x
val y = node.y

if (x >= 0 && y >= 0) {
if (x >= 0 && y >= 0 && x <= maxWidth.value && y <= maxHeight.value) {
drawText(
textMeasurer,
text = " ${node.label} ",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ internal fun Map<String, List<ItemData>>.toGraph(maxWidth: Float, maxHeight: Flo
Node(
id = item.id,
label = item.title,
initialX = (200..700).random().toFloat() / 1000 * maxWidth * 2,
initialY = (300..600).random().toFloat() / 1000 * maxHeight * 2,
initialX = (150..300).random().toFloat() / 1000 * maxWidth * 2,
initialY = (200..350).random().toFloat() / 1000 * maxHeight * 2,
isFolder = item.isFolder,
selected = item.selected
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,16 @@ class DocumentsGraphViewModel(
@OptIn(ExperimentalCoroutinesApi::class)
private val graphState: StateFlow<Graph> by lazy {
_selectedOrigin.map { origin ->
graphRepository.loadAllDocumentsAsAdjacencyList("disconnected_user")
val result = graphRepository.loadAllDocumentsAsAdjacencyList("disconnected_user")

val nodes = result.values.flatten()
val isSmall = nodes.size <= 12

if (isSmall) {
_selectedNodes.value = nodes.map { it.id }.toSet()
}

result
}.map { map ->
map.mapKeys { it.key.id }.addRoot().toGraph(maxWidth, maxHeight)
}.flatMapLatest { graph ->
Expand Down Expand Up @@ -107,7 +116,7 @@ class DocumentsGraphViewModel(
}

private fun applyChargeForce(nodes: List<Node>) {
val chargeStrength = 3000f // Reduced from 5000f
val chargeStrength = if (nodes.size < 10) 100f else 2000f

for (i in nodes.indices) {
for (j in i + 1 until nodes.size) {
Expand All @@ -131,7 +140,7 @@ class DocumentsGraphViewModel(
private fun applyCenteringForce(nodes: List<Node>) {
val centerX = maxWidth / 2
val centerY = maxHeight / 2
val strength = 0.0003f
val strength = 0.003f

for (node in nodes) {
node.vx += (centerX - node.x) * strength
Expand Down