Skip to content

Commit

Permalink
fix: add many-body force to help separate nodes; draw text under node…
Browse files Browse the repository at this point in the history
… rather than from center; handle dark mode; update node colors

https://developer.android.com/develop/ui/compose/designsystems/custom#extending-material
  • Loading branch information
lydavid committed Aug 2, 2024
1 parent 8d902cf commit 151bbcf
Show file tree
Hide file tree
Showing 20 changed files with 334 additions and 228 deletions.
7 changes: 7 additions & 0 deletions docs/all_features.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
| [Browse](#browse) ||||
| [Collections](#collections) ||||
| [Images](#images) ||||
| [Graph](#graph) ||||
| [MusicBrainz Login](#musicbrainz-login) ||| ⬜️ |
| Pixel Now Playing History ||||
| [Search MusicBrainz](#search-musicbrainz) ||||
Expand Down Expand Up @@ -92,6 +93,12 @@ See below for the supported types of collections.
| series ||
| work ||

## Graph

Artist/recording graph showing which artists and recordings a given artist collaborated on.
This is currently limited to artists credited on the recording, which are primarily performers.
It does not include composers.

## MusicBrainz Login

From the app's settings, you can login to your MusicBrianz account.
Expand Down
16 changes: 0 additions & 16 deletions shared/feature/graph/config/baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,7 @@
<ID>MagicNumber:Cover.kt$3</ID>
<ID>MagicNumber:Force.kt$0.5</ID>
<ID>MagicNumber:Percent.kt$Percent$100</ID>
<ID>MagicNumber:Render.kt$0xFF009688</ID>
<ID>MagicNumber:Render.kt$0xFF00BCD4</ID>
<ID>MagicNumber:Render.kt$0xFF2196F3</ID>
<ID>MagicNumber:Render.kt$0xFF3F51B5</ID>
<ID>MagicNumber:Render.kt$0xFF4CAF50</ID>
<ID>MagicNumber:Render.kt$0xFF607D8B</ID>
<ID>MagicNumber:Render.kt$0xFF795548</ID>
<ID>MagicNumber:Render.kt$0xFF8BC34A</ID>
<ID>MagicNumber:Render.kt$0xFF9C27B0</ID>
<ID>MagicNumber:Render.kt$0xFFE91E63</ID>
<ID>MagicNumber:Render.kt$0xFFF44336</ID>
<ID>MagicNumber:Render.kt$0xFFFF5722</ID>
<ID>MagicNumber:Render.kt$0xFFFF9800</ID>
<ID>MagicNumber:Render.kt$0xFFFFC107</ID>
<ID>NestedBlockDepth:ForceCollision.kt$ForceCollision$private fun applyForce( quad: QuadtreeNode&lt;ForceNode&lt;D&gt;&gt;, x0: Double, y0: Double, x1: Double, y1: Double, ): Boolean</ID>
<ID>NoWildcardImports:ForceSimulation.kt$import kotlin.math.*</ID>
<ID>ReturnCount:Add.kt$private fun &lt;D&gt; Quadtree&lt;D&gt;._add(x: Double, y: Double, datum: D)</ID>
<ID>ReturnCount:ForceNBody.kt$ForceNBody$private fun applyForce( quad: QuadtreeNode&lt;ForceNode&lt;D&gt;&gt;, x0: Double, y0: Double, x1: Double, y1: Double, ): Boolean</ID>
<ID>TopLevelPropertyNaming:ForceSimulation.kt$private const val initialRadius = 10.0</ID>
Expand All @@ -48,6 +33,5 @@
<ID>UnusedParameter:ForceNBody.kt$ForceNBody$x1: Double</ID>
<ID>UnusedParameter:ForceNBody.kt$ForceNBody$y0: Double</ID>
<ID>UnusedParameter:ForceNBody.kt$ForceNBody$y1: Double</ID>
<ID>WildcardImport:ForceSimulation.kt$import kotlin.math.*</ID>
</CurrentIssues>
</SmellBaseline>
Original file line number Diff line number Diff line change
Expand Up @@ -12,62 +12,62 @@ internal fun PreviewArtistCollaborationGraphUi() {
PreviewTheme {
Surface {
ArtistCollaborationGraphUi(
links = listOf(
GraphLink(
edges = listOf(
GraphEdge(
x0 = -7.010541039928112,
y0 = 148.53020774179376,
x1 = 34.442058589085114,
y1 = -98.70221015951988,
),
GraphLink(
GraphEdge(
x0 = -7.010541039928112,
y0 = 148.53020774179376,
x1 = 107.57529725372356,
y1 = -59.45777853885758,
),
GraphLink(
GraphEdge(
x0 = -7.010541039928112,
y0 = 148.53020774179376,
x1 = -47.58660614527603,
y1 = -111.3471010505625,
),
GraphLink(
GraphEdge(
x0 = -87.41917955713517,
y0 = 120.97761778046068,
x1 = 107.57529725372356,
y1 = -59.45777853885758,
),
GraphLink(
GraphEdge(
x0 = -87.41917955713517,
y0 = 120.97761778046068,
x1 = -47.58660614527603,
y1 = -111.3471010505625,
),
GraphLink(
GraphEdge(
x0 = -7.010541039928112,
y0 = 148.53020774179376,
x1 = 34.442058589085114,
y1 = -98.70221015951988,
),
GraphLink(
GraphEdge(
x0 = -87.41917955713517,
y0 = 120.97761778046068,
x1 = 107.57529725372356,
y1 = -59.45777853885758,
),
GraphLink(
GraphEdge(
x0 = -7.010541039928112,
y0 = 148.53020774179376,
x1 = 107.57529725372356,
y1 = -59.45777853885758,
),
GraphLink(
GraphEdge(
x0 = -87.41917955713517,
y0 = 120.97761778046068,
x1 = -47.58660614527603,
y1 = -111.3471010505625,
),
GraphLink(
GraphEdge(
x0 = -7.010541039928112,
y0 = 148.53020774179376,
x1 = -47.58660614527603,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class ArtistCollaborationGraphSimulationTest {
val initialState = awaitItem()

assertEquals(0, initialState.nodes.size)
assertEquals(0, initialState.links.size)
assertEquals(0, initialState.edges.size)

simulation.step()
val state = awaitItem()
Expand All @@ -42,7 +42,7 @@ class ArtistCollaborationGraphSimulationTest {
assertEquals(9, state.nodes.filter { it.entity == MusicBrainzEntity.ARTIST }.size)
assertEquals(17, state.nodes.filter { it.entity == MusicBrainzEntity.RECORDING }.size)

assertEquals(42, state.links.size)
assertEquals(42, state.edges.size)

cancelAndConsumeRemainingEvents()
}
Expand All @@ -56,7 +56,7 @@ class ArtistCollaborationGraphSimulationTest {
val initialState = awaitItem()

assertEquals(0, initialState.nodes.size)
assertEquals(0, initialState.links.size)
assertEquals(0, initialState.edges.size)

simulation.step()
val state1 = awaitItem()
Expand All @@ -65,7 +65,7 @@ class ArtistCollaborationGraphSimulationTest {
val state2 = awaitItem()

assertNotEquals(state1.nodes, state2.nodes)
assertNotEquals(state1.links, state2.links)
assertNotEquals(state1.edges, state2.edges)

cancelAndConsumeRemainingEvents()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ internal class ArtistCollaborationGraphPresenter(

return ArtistCollaborationGraphUiState(
artistName = screen.name,
links = graphState.links,
edges = graphState.edges,
nodes = graphState.nodes,
eventSink = ::eventSink,
)
Expand All @@ -84,7 +84,7 @@ internal class ArtistCollaborationGraphPresenter(
@Stable
internal data class ArtistCollaborationGraphUiState(
val artistName: String,
val links: List<GraphLink> = listOf(),
val edges: List<GraphEdge> = listOf(),
val nodes: List<GraphNode> = listOf(),
val eventSink: (ArtistCollaborationGraphUiEvent) -> Unit,
) : CircuitUiState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ data class GraphNode(
}
}

data class GraphLink(
data class GraphEdge(
val x0: Double = 0.0,
val y0: Double = 0.0,
val x1: Double = 0.0,
val y1: Double = 0.0,
)

data class GraphSimulationUiState(
val links: List<GraphLink> = listOf(),
val edges: List<GraphEdge> = listOf(),
val nodes: List<GraphNode> = listOf(),
)

Expand All @@ -46,7 +46,7 @@ private data class ArtistRecording(
private const val MIN_RADIUS = 10.0
private const val LINK_DISTANCE = 250.0

// private const val MANY_BODY_STRENGTH = -30.0
private const val MANY_BODY_STRENGTH = -60.0
private const val COLLISION_DISTANCE = 30.0

class ArtistCollaborationGraphSimulation {
Expand Down Expand Up @@ -75,11 +75,11 @@ class ArtistCollaborationGraphSimulation {
)
}

// forceNBody {
// strengthGet = {
// MANY_BODY_STRENGTH
// }
// }
forceNBody {
strengthGet = {
MANY_BODY_STRENGTH
}
}

forceLinks = forceLink {
linkGet = {
Expand Down Expand Up @@ -161,8 +161,8 @@ class ArtistCollaborationGraphSimulation {
simulation.step(1)

_uiState.update { uiState ->
val links = forceLinks?.links?.map { link ->
GraphLink(
val edges = forceLinks?.links?.map { link ->
GraphEdge(
x0 = link.source.x,
x1 = link.target.x,
y0 = link.source.y,
Expand All @@ -178,7 +178,7 @@ class ArtistCollaborationGraphSimulation {
}

uiState.copy(
links = links,
edges = edges,
nodes = nodes,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ package ly.david.musicsearch.shared.feature.graph
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
Expand All @@ -17,19 +17,17 @@ import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.drawText
import androidx.compose.ui.text.rememberTextMeasurer
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import ly.david.musicsearch.shared.feature.graph.viz.render
import ly.david.musicsearch.shared.feature.graph.viz.compose.renderEdge
import ly.david.musicsearch.shared.feature.graph.viz.compose.renderNode
import ly.david.musicsearch.shared.feature.graph.viz.compose.renderText
import ly.david.musicsearch.ui.common.topappbar.ScrollableTopAppBar
import ly.david.musicsearch.ui.core.LocalStrings
import ly.david.musicsearch.ui.core.theme.ExtendedColors
import ly.david.musicsearch.ui.core.theme.LocalExtendedColors
import ly.david.musicsearch.ui.core.theme.getSubTextColor

@OptIn(ExperimentalMaterial3Api::class)
Expand All @@ -56,7 +54,7 @@ internal fun ArtistCollaborationGraphUi(
},
) { innerPadding ->
ArtistCollaborationGraphUi(
links = state.links,
edges = state.edges,
nodes = state.nodes,
modifier = Modifier.padding(innerPadding),
onClick = { tapOffset, drawOffset ->
Expand Down Expand Up @@ -86,7 +84,7 @@ internal fun ArtistCollaborationGraphUi(

@Composable
internal fun ArtistCollaborationGraphUi(
links: List<GraphLink>,
edges: List<GraphEdge>,
nodes: List<GraphNode>,
modifier: Modifier = Modifier,
onClick: (tapOffset: Offset, drawOffset: Offset) -> Unit = { _, _ -> },
Expand All @@ -95,9 +93,9 @@ internal fun ArtistCollaborationGraphUi(
var center by remember { mutableStateOf(Offset.Zero) }
val textMeasurer = rememberTextMeasurer()

// TODO: AppPreferences.useDarkTheme()
val isDark = isSystemInDarkTheme()
val lineColor = getSubTextColor()
val extendedColors: ExtendedColors = LocalExtendedColors.current
val textColor = MaterialTheme.colorScheme.onBackground
val edgeColor = getSubTextColor()

val currentOnClick by rememberUpdatedState(onClick)

Expand All @@ -124,39 +122,25 @@ internal fun ArtistCollaborationGraphUi(
center = this.center
val drawOffset = panOffset + center

links
.forEach { node ->
render(
lineNode = node,
offset = drawOffset,
color = lineColor,
)
}
nodes
.forEach { node ->
render(
graphNode = node,
offset = drawOffset,
)
val measuredText =
textMeasurer.measure(
text = node.name,
constraints = Constraints.fixed(
width = (size.width / 3f).toInt(),
height = (size.height / 3f).toInt(),
),
overflow = TextOverflow.Ellipsis,
style = TextStyle(fontSize = 13.sp),
)

drawText(
textLayoutResult = measuredText,
color = if (isDark) Color.White else Color.Black,
topLeft = Offset(
node.x.dp.toPx(),
node.y.dp.toPx(),
) + drawOffset,
)
}
edges.forEach { edge ->
renderEdge(
edge = edge,
offset = drawOffset,
color = edgeColor,
)
}
nodes.forEach { node ->
renderNode(
node = node,
offset = drawOffset,
extendedColors = extendedColors,
)
renderText(
node = node,
offset = drawOffset,
color = textColor,
textMeasurer = textMeasurer,
)
}
}
}
Loading

0 comments on commit 151bbcf

Please sign in to comment.