visualize parent's step with a grey color

visualizer
maxime.batista 1 year ago committed by maxime
parent ec57f0b7de
commit fc9a2181e9

@ -26,9 +26,10 @@ private const val ArrowHeadHeightPx = 35F
fun drawActions(
drawer: ContentDrawScope,
content: StepContent,
offsets: Map<String, Vector>,
offsets: Map<ComponentId, Vector>,
area: Rect,
playersPixelsRadius: Float
playersPixelsRadius: Float,
color: Color
) {
for (component in content.components) {
val originPos = offsets[component.id]!!
@ -79,7 +80,7 @@ fun drawActions(
path.lineTo(pathEnd.x, pathEnd.y)
drawer.drawPath(
path = path,
color = Color.Black,
color = color,
style = strokeStyle
)
} else {
@ -92,7 +93,7 @@ fun drawActions(
type == ActionType.Dribble,
playersPixelsRadius
),
color = Color.Black,
color = color,
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.StepContent
import net.engawapg.lib.zoomable.ZoomState
import net.engawapg.lib.zoomable.rememberZoomState
import net.engawapg.lib.zoomable.zoomable
data class BasketCourtStates(
val componentsOffsets: MutableMap<ComponentId, Offset>,
val stepComponentsOffsets: MutableMap<ComponentId, Offset>,
val parentComponentsOffsets: MutableMap<ComponentId, Offset>,
val courtArea: MutableState<Rect>,
val zoomState: ZoomState
)
@ -40,6 +40,7 @@ data class BasketCourtStates(
@Composable
fun BasketCourt(
content: StepContent,
parentContent: StepContent?,
type: CourtType,
modifier: Modifier,
state: BasketCourtStates
@ -50,12 +51,6 @@ fun BasketCourt(
}
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(
modifier = modifier,
@ -74,6 +69,35 @@ fun BasketCourt(
.background(Color.White)
)
CourtContent(
courtAreaState = state.courtArea,
content = content,
offsets = state.stepComponentsOffsets,
isFromParent = false
)
if (parentContent != null) {
CourtContent(
courtAreaState = state.courtArea,
content = parentContent,
offsets = state.parentComponentsOffsets,
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()
@ -83,32 +107,32 @@ fun BasketCourt(
}
.drawWithContent {
val relativeOffsets =
componentsOffset.mapValues { (it.value - courtArea.topLeft).toVector() }
drawActions(this, content, relativeOffsets, courtArea, playersPixelsRadius)
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) {
for (component in content.components) {
val componentModifier = Modifier
.onGloballyPositioned {
if (!componentsOffset.containsKey(component.id))
componentsOffset[component.id] = it.boundsInRoot().center
if (!offsets.containsKey(component.id))
offsets[component.id] = it.boundsInRoot().center
}
when (component) {
is PlayerLike -> {
val info = getPlayerInfo(component, content)
PlayerPiece(
player = info,
isFromParent = isFromParent,
modifier = componentModifier
.align(info.pos.toBiasAlignment())
)
}
is BallComponent -> BallPiece(
modifier = modifier
modifier = componentModifier
.align(
component.pos
.toPos()
@ -119,5 +143,3 @@ fun BasketCourt(
}
}
}
}
}

@ -20,9 +20,9 @@ import com.iqball.app.ui.theme.Opponents
const val PlayerPieceDiameterDp = 25
@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(
shape = CircleShape,

@ -1,3 +1,17 @@
package com.iqball.app.model.tactic
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.tactic.ComponentId
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.getParent
import com.iqball.app.net.service.TacticService
import com.iqball.app.session.Token
import kotlinx.coroutines.runBlocking
import net.engawapg.lib.zoomable.ZoomState
import net.engawapg.lib.zoomable.rememberZoomState
import java.time.Instant
import java.time.LocalDateTime
@ -62,19 +65,16 @@ fun VisualizerPage(
val dataEither = remember { initializeVisualizer(service, auth, tacticId) }
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
is Either.Left -> return Text(text = dataEither.value)
is Either.Right -> dataEither.value
}
val screenOrientation = LocalConfiguration.current.orientation
var selectedStepId by rememberSaveable { mutableIntStateOf(rootStep.id) }
val content = remember(selectedStepId) {
runBlocking {
val result = service.getTacticStepContent(auth, tacticId, selectedStepId).onLeft {
fun getStepContent(step: Int): StepContent = runBlocking {
val result = service.getTacticStepContent(auth, tacticId, step).onLeft {
Log.e(
"received error response from server when retrieving root content: {}",
"received error response from server when retrieving step content: {}",
it.toString()
)
}
@ -83,26 +83,34 @@ fun VisualizerPage(
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 {
VisualizerHeader(title = info.name, showTree)
when (screenOrientation) {
Configuration.ORIENTATION_PORTRAIT -> StepsTree(root = rootStep,
Configuration.ORIENTATION_PORTRAIT -> StepsTree(root = stepsTree,
selectedNodeId = selectedStepId,
onNodeSelected = { selectedStepId = it.id })
Configuration.ORIENTATION_LANDSCAPE -> {
val courtOffsets =
val stepOffsets =
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 courtZoomState = rememberZoomState()
remember(showTree.value, content) {
runBlocking {
courtZoomState.reset()
}
}
val courtZoomState = remember(showTree.value, selectedStepId) { ZoomState() }
val courtModifier =
if (showTree.value) Modifier.width(IntrinsicSize.Min) else Modifier.fillMaxWidth()
@ -110,12 +118,13 @@ fun VisualizerPage(
Row(modifier = Modifier.background(Color.LightGray)) {
BasketCourt(
content = content,
parentContent,
type = info.type,
modifier = courtModifier,
state = BasketCourtStates(courtOffsets, courtArea, courtZoomState)
state = BasketCourtStates(stepOffsets, parentOffsets, courtArea, courtZoomState)
)
if (showTree.value) {
StepsTree(root = rootStep,
StepsTree(root = stepsTree,
selectedNodeId = selectedStepId,
onNodeSelected = { selectedStepId = it.id })
}

Loading…
Cancel
Save