visualize parent's step with a grey color

maxime.batista 1 year ago
parent 53d3254f7b
commit 2e41bd50d9

@ -26,9 +26,10 @@ private const val ArrowHeadHeightPx = 35F
fun drawActions( fun drawActions(
drawer: ContentDrawScope, drawer: ContentDrawScope,
content: StepContent, content: StepContent,
offsets: Map<String, Vector>, offsets: Map<ComponentId, Vector>,
area: Rect, area: Rect,
playersPixelsRadius: Float playersPixelsRadius: Float,
color: Color
) { ) {
for (component in content.components) { for (component in content.components) {
val originPos = offsets[component.id]!! val originPos = offsets[component.id]!!
@ -79,7 +80,7 @@ fun drawActions(
path.lineTo(pathEnd.x, pathEnd.y) path.lineTo(pathEnd.x, pathEnd.y)
drawer.drawPath( drawer.drawPath(
path = path, path = path,
color = Color.Black, color = color,
style = strokeStyle style = strokeStyle
) )
} else { } else {
@ -92,7 +93,7 @@ fun drawActions(
type == ActionType.Dribble, type == ActionType.Dribble,
playersPixelsRadius playersPixelsRadius
), ),
color = Color.Black, color = color,
style = strokeStyle style = strokeStyle
) )
} }

@ -28,11 +28,11 @@ import com.iqball.app.model.tactic.CourtType
import com.iqball.app.model.tactic.PlayerLike import com.iqball.app.model.tactic.PlayerLike
import com.iqball.app.model.tactic.StepContent import com.iqball.app.model.tactic.StepContent
import net.engawapg.lib.zoomable.ZoomState import net.engawapg.lib.zoomable.ZoomState
import net.engawapg.lib.zoomable.rememberZoomState
import net.engawapg.lib.zoomable.zoomable import net.engawapg.lib.zoomable.zoomable
data class BasketCourtStates( data class BasketCourtStates(
val componentsOffsets: MutableMap<ComponentId, Offset>, val stepComponentsOffsets: MutableMap<ComponentId, Offset>,
val parentComponentsOffsets: MutableMap<ComponentId, Offset>,
val courtArea: MutableState<Rect>, val courtArea: MutableState<Rect>,
val zoomState: ZoomState val zoomState: ZoomState
) )
@ -40,6 +40,7 @@ data class BasketCourtStates(
@Composable @Composable
fun BasketCourt( fun BasketCourt(
content: StepContent, content: StepContent,
parentContent: StepContent?,
type: CourtType, type: CourtType,
modifier: Modifier, modifier: Modifier,
state: BasketCourtStates state: BasketCourtStates
@ -50,12 +51,6 @@ fun BasketCourt(
} }
val zoomState = state.zoomState val zoomState = state.zoomState
val components = content.components
val componentsOffset = state.componentsOffsets
var courtArea by state.courtArea
val playersPixelsRadius = LocalDensity.current.run { PlayerPieceDiameterDp.dp.toPx() / 2 }
Box( Box(
modifier = modifier, modifier = modifier,
@ -74,49 +69,76 @@ fun BasketCourt(
.background(Color.White) .background(Color.White)
) )
Box( CourtContent(
modifier = Modifier courtAreaState = state.courtArea,
.fillMaxSize() content = content,
.onGloballyPositioned { offsets = state.stepComponentsOffsets,
if (courtArea == Rect.Zero) isFromParent = false
courtArea = it.boundsInRoot() )
} if (parentContent != null) {
.drawWithContent { CourtContent(
val relativeOffsets = courtAreaState = state.courtArea,
componentsOffset.mapValues { (it.value - courtArea.topLeft).toVector() } content = parentContent,
drawActions(this, content, relativeOffsets, courtArea, playersPixelsRadius) offsets = state.parentComponentsOffsets,
drawContent() isFromParent = true
} )
) { }
}
}
}
@Composable
private fun CourtContent(
courtAreaState: MutableState<Rect>,
content: StepContent,
offsets: MutableMap<ComponentId, Offset>,
isFromParent: Boolean
) {
var courtArea by courtAreaState
val playersPixelsRadius = LocalDensity.current.run { PlayerPieceDiameterDp.dp.toPx() / 2 }
Box(
modifier = Modifier
.fillMaxSize()
.onGloballyPositioned {
if (courtArea == Rect.Zero)
courtArea = it.boundsInRoot()
}
.drawWithContent {
val relativeOffsets =
offsets.mapValues { (it.value - courtArea.topLeft).toVector() }
drawActions(this, content, relativeOffsets, courtArea, playersPixelsRadius, if (isFromParent) Color.Gray else Color.Black)
drawContent()
}
) {
for (component in components) {
val componentModifier = Modifier
.onGloballyPositioned {
if (!componentsOffset.containsKey(component.id))
componentsOffset[component.id] = it.boundsInRoot().center
}
when (component) {
is PlayerLike -> {
val info = getPlayerInfo(component, content)
PlayerPiece(
player = info,
modifier = componentModifier
.align(info.pos.toBiasAlignment())
)
}
is BallComponent -> BallPiece( for (component in content.components) {
modifier = modifier val componentModifier = Modifier
.align( .onGloballyPositioned {
component.pos if (!offsets.containsKey(component.id))
.toPos() offsets[component.id] = it.boundsInRoot().center
.toBiasAlignment() }
) when (component) {
) is PlayerLike -> {
} val info = getPlayerInfo(component, content)
PlayerPiece(
player = info,
isFromParent = isFromParent,
modifier = componentModifier
.align(info.pos.toBiasAlignment())
)
} }
is BallComponent -> BallPiece(
modifier = componentModifier
.align(
component.pos
.toPos()
.toBiasAlignment()
)
)
} }
} }
} }

@ -20,9 +20,9 @@ import com.iqball.app.ui.theme.Opponents
const val PlayerPieceDiameterDp = 25 const val PlayerPieceDiameterDp = 25
@Composable @Composable
fun PlayerPiece(player: PlayerInfo, modifier: Modifier = Modifier) { fun PlayerPiece(player: PlayerInfo, modifier: Modifier = Modifier, isFromParent: Boolean) {
val color = if (player.team === PlayerTeam.Allies) Allies else Opponents val color = if (isFromParent) Color.LightGray else if (player.team === PlayerTeam.Allies) Allies else Opponents
return Surface( return Surface(
shape = CircleShape, shape = CircleShape,

@ -1,3 +1,17 @@
package com.iqball.app.model.tactic package com.iqball.app.model.tactic
data class StepNodeInfo(val id: Int, val children: List<StepNodeInfo>) data class StepNodeInfo(val id: Int, val children: List<StepNodeInfo>)
fun getParent(root: StepNodeInfo, child: Int): StepNodeInfo? {
for (children in root.children) {
if (children.id == child) {
return root
}
val result = getParent(children, child)
if (result != null)
return result
}
return null
}

@ -39,10 +39,13 @@ import com.iqball.app.component.StepsTree
import com.iqball.app.model.TacticInfo import com.iqball.app.model.TacticInfo
import com.iqball.app.model.tactic.ComponentId import com.iqball.app.model.tactic.ComponentId
import com.iqball.app.model.tactic.CourtType import com.iqball.app.model.tactic.CourtType
import com.iqball.app.model.tactic.StepContent
import com.iqball.app.model.tactic.StepNodeInfo import com.iqball.app.model.tactic.StepNodeInfo
import com.iqball.app.model.tactic.getParent
import com.iqball.app.net.service.TacticService import com.iqball.app.net.service.TacticService
import com.iqball.app.session.Token import com.iqball.app.session.Token
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import net.engawapg.lib.zoomable.ZoomState
import net.engawapg.lib.zoomable.rememberZoomState import net.engawapg.lib.zoomable.rememberZoomState
import java.time.Instant import java.time.Instant
import java.time.LocalDateTime import java.time.LocalDateTime
@ -62,47 +65,52 @@ fun VisualizerPage(
val dataEither = remember { initializeVisualizer(service, auth, tacticId) } val dataEither = remember { initializeVisualizer(service, auth, tacticId) }
val showTree = remember { mutableStateOf(true) } val showTree = remember { mutableStateOf(true) }
val (info, rootStep) = when (dataEither) { val (info, stepsTree) = when (dataEither) {
// On error return a text to print it to the user // On error return a text to print it to the user
is Either.Left -> return Text(text = dataEither.value) is Either.Left -> return Text(text = dataEither.value)
is Either.Right -> dataEither.value is Either.Right -> dataEither.value
} }
val screenOrientation = LocalConfiguration.current.orientation fun getStepContent(step: Int): StepContent = runBlocking {
var selectedStepId by rememberSaveable { mutableIntStateOf(rootStep.id) } val result = service.getTacticStepContent(auth, tacticId, step).onLeft {
val content = remember(selectedStepId) { Log.e(
runBlocking { "received error response from server when retrieving step content: {}",
val result = service.getTacticStepContent(auth, tacticId, selectedStepId).onLeft { it.toString()
Log.e( )
"received error response from server when retrieving root content: {}", }
it.toString() when (result) {
) is Either.Left -> throw Error("Unexpected error")
} is Either.Right -> result.value
when (result) {
is Either.Left -> throw Error("Unexpected error")
is Either.Right -> result.value
}
} }
} }
val screenOrientation = LocalConfiguration.current.orientation
var selectedStepId by rememberSaveable { mutableIntStateOf(stepsTree.id) }
val (content, parentContent) = remember(selectedStepId) {
val parentId = getParent(stepsTree, selectedStepId)?.id
Pair(
getStepContent(selectedStepId),
if (parentId == null) null else getStepContent(parentId)
)
}
Column { Column {
VisualizerHeader(title = info.name, showTree) VisualizerHeader(title = info.name, showTree)
when (screenOrientation) { when (screenOrientation) {
Configuration.ORIENTATION_PORTRAIT -> StepsTree(root = rootStep, Configuration.ORIENTATION_PORTRAIT -> StepsTree(root = stepsTree,
selectedNodeId = selectedStepId, selectedNodeId = selectedStepId,
onNodeSelected = { selectedStepId = it.id }) onNodeSelected = { selectedStepId = it.id })
Configuration.ORIENTATION_LANDSCAPE -> { Configuration.ORIENTATION_LANDSCAPE -> {
val courtOffsets = val stepOffsets =
remember(showTree.value, content) { mutableStateMapOf<ComponentId, Offset>() } remember(showTree.value, content) { mutableStateMapOf<ComponentId, Offset>() }
val parentOffsets =
remember(showTree.value, parentContent) { mutableStateMapOf<ComponentId, Offset>() }
val courtArea = remember(showTree.value) { mutableStateOf(Rect.Zero) } val courtArea = remember(showTree.value) { mutableStateOf(Rect.Zero) }
val courtZoomState = rememberZoomState() val courtZoomState = remember(showTree.value, selectedStepId) { ZoomState() }
remember(showTree.value, content) {
runBlocking {
courtZoomState.reset()
}
}
val courtModifier = val courtModifier =
if (showTree.value) Modifier.width(IntrinsicSize.Min) else Modifier.fillMaxWidth() if (showTree.value) Modifier.width(IntrinsicSize.Min) else Modifier.fillMaxWidth()
@ -110,12 +118,13 @@ fun VisualizerPage(
Row(modifier = Modifier.background(Color.LightGray)) { Row(modifier = Modifier.background(Color.LightGray)) {
BasketCourt( BasketCourt(
content = content, content = content,
parentContent,
type = info.type, type = info.type,
modifier = courtModifier, modifier = courtModifier,
state = BasketCourtStates(courtOffsets, courtArea, courtZoomState) state = BasketCourtStates(stepOffsets, parentOffsets, courtArea, courtZoomState)
) )
if (showTree.value) { if (showTree.value) {
StepsTree(root = rootStep, StepsTree(root = stepsTree,
selectedNodeId = selectedStepId, selectedNodeId = selectedStepId,
onNodeSelected = { selectedStepId = it.id }) onNodeSelected = { selectedStepId = it.id })
} }

Loading…
Cancel
Save