Fixes player placement around the table

hh
Laurent 5 years ago
parent f8fccc72cf
commit de4e6e45fe
  1. 2
      app/build.gradle
  2. 25
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/HandHistoryActivity.kt
  3. 255
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/ReplayerAnimator.kt
  4. 29
      app/src/main/java/net/pokeranalytics/android/ui/modules/handhistory/replayer/TableDrawer.kt
  5. 112
      app/src/main/java/net/pokeranalytics/android/util/MathUtils.kt
  6. 1
      app/src/main/res/values/colors.xml

@ -100,7 +100,7 @@ dependencies {
// Android
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.4.0-alpha01'
implementation 'androidx.core:core-ktx:1.3.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'

@ -72,11 +72,7 @@ class HandHistoryActivity : BaseActivity() {
val attached = intent.getBooleanExtra(IntentKey.ATTACHED.keyName, false)
val fragment = HandHistoryFragment.newInstance(handHistoryId, sessionId, attached)
val fragmentTransaction = supportFragmentManager.beginTransaction()
fragmentTransaction.add(R.id.container, fragment)
fragmentTransaction.commit()
showFragment(fragment, R.id.container)
this.fragment = fragment
}
@ -85,22 +81,19 @@ class HandHistoryActivity : BaseActivity() {
// val attached = intent.getBooleanExtra(IntentKey.ATTACHED.keyName, false)
val fragment = ReplayerFragment.newInstance(handHistoryId)
val fragmentTransaction = supportFragmentManager.beginTransaction()
fragmentTransaction.add(R.id.container, fragment)
fragmentTransaction.commit()
showFragment(fragment, R.id.container)
this.fragment = fragment
}
override fun onBackPressed() {
var shouldShowDataLossWarning = false
this.fragment?.let { hhFragment ->
if ((hhFragment as? HandHistoryFragment)?.isEditing == true) {
shouldShowDataLossWarning = true
}
}
val shouldShowDataLossWarning = ((this.fragment as? HandHistoryFragment)?.isEditing == true)
// this.fragment?.let { hhFragment ->
// if ((hhFragment as? HandHistoryFragment)?.isEditing == true) {
// shouldShowDataLossWarning = true
// }
// }
if (shouldShowDataLossWarning) {

@ -9,9 +9,10 @@ import net.pokeranalytics.android.model.handhistory.Street
import net.pokeranalytics.android.model.realm.handhistory.HandHistory
import net.pokeranalytics.android.ui.modules.handhistory.model.ActionList
import net.pokeranalytics.android.ui.modules.handhistory.model.ComputedAction
import net.pokeranalytics.android.util.MathUtils
import timber.log.Timber
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
class ReplayerAnimator(var handHistory: HandHistory, var export: Boolean) {
@ -247,14 +248,6 @@ class ReplayerAnimator(var handHistory: HandHistory, var export: Boolean) {
this.tableCornerXRadius = this.tableRect.width() / 3
this.tableCornerYRadius = this.tableRect.height() / 3
// for (i in (0 until handHistory.numberOfPlayers)) {
//
// val angle = i * (2 * Math.PI / handHistory.numberOfPlayers)
// val x =
// val y =
//
// }
// this.tableCornerRadius = (if (portrait) width else height) / 8
@ -290,96 +283,14 @@ class ReplayerAnimator(var handHistory: HandHistory, var export: Boolean) {
this.totalPotTextPoint = TextPoint(centerX, centerY - 2f * chipTextSize, chipTextSize)
val playerCount = this.handHistory.numberOfPlayers
// number of players in this order: bottom / left / top / right
val repartition = when (playerCount) {
2 -> mutableListOf(1, 0, 1, 0)
3 -> mutableListOf(1, 1, 0, 1)
4 -> mutableListOf(1, 1, 1, 1)
5 -> mutableListOf(1, 1, 2, 1)
6 -> mutableListOf(2, 1, 2, 1)
7 -> mutableListOf(2, 1, 3, 1)
8 -> mutableListOf(3, 1, 3, 1)
9 -> mutableListOf(3, 1, 4, 1)
10 -> mutableListOf(4, 1, 4, 1)
else -> throw PAIllegalStateException("can't happen")
}
if (repartition.sum() != playerCount) {
throw PAIllegalStateException("Problem in the $playerCount players repartition: $repartition")
}
if (portrait && playerCount > 4) { repartition.reverse() } // adjustment for portrait vs landscape
val chipsVerticalOffsetFromStackCenter = this.playerItemsHeight * 0.9f
val chipsHorizontalOffsetFromStackCenter = this.tableHPadding * 1.2f
var pIndex = -1
repartition.forEachIndexed { index, count ->
val xItemSpacing = width / (count + 1)
val yItemSpacing = height / (count + 1)
var rectCenterX: Float
var rectCenterY: Float
var chipXOffset: Float
var chipYOffset: Float
var chipTextYOffsetCoef = 0f
var circleOffset = playerItemsHeight * 1.75f
for (i in 1..count) {
pIndex += 1
Timber.d("Creating dimensions for player $pIndex")
// when the position is in a corner, because we have 3/4 players
// we need an additional offset direction to display the chips
val cornerDirection = when {
count > 2 && i == 1 -> { 1 }
count > 2 && i == count -> { - 1 }
else -> { 0 }
}
when (index) {
0 -> { // bottom
rectCenterX = xItemSpacing * i
rectCenterY = height - this.tableVPadding
circleOffset *= -1
chipXOffset = cornerDirection * chipsHorizontalOffsetFromStackCenter
chipYOffset = -1 * chipsVerticalOffsetFromStackCenter
chipTextYOffsetCoef = 1f
}
1 -> { // left
rectCenterY = if (count == 4 && (i == 2 || i == 3)) {
height - yItemSpacing - this.playerItemsHeight * 1.2f - pzHeight * (i - 2)
} else {
height - yItemSpacing * i
}
rectCenterX = this.tableHPadding
// rectCenterY = height - yItemSpacing * i
chipXOffset = 1 * chipsHorizontalOffsetFromStackCenter
chipYOffset = cornerDirection * -1 * chipsVerticalOffsetFromStackCenter
chipTextYOffsetCoef = 1f * (i - 1) / count
if (count == 4 && i == 1) {
circleOffset *= -1
} else {
circleOffset = abs(circleOffset)
}
val positions = MathUtils.positionsAroundRoundedRectangle(playerCount, this.tableRect, this.tableCornerXRadius, this.tableCornerYRadius)
for (i in (0 until playerCount)) {
}
2 -> { // top
rectCenterX = xItemSpacing * i
rectCenterY = this.tableVPadding
chipXOffset = cornerDirection * -1 * chipsHorizontalOffsetFromStackCenter
chipYOffset = 1 * chipsVerticalOffsetFromStackCenter
}
3 -> { // right
rectCenterX = width - this.tableHPadding
rectCenterY = yItemSpacing * i
chipXOffset = -1 * chipsHorizontalOffsetFromStackCenter
chipYOffset = cornerDirection * chipsVerticalOffsetFromStackCenter
chipTextYOffsetCoef = 1f * (count - i + 1) / count
}
else -> throw PAIllegalStateException("can't happen")
}
val point = positions[i].first
val bottomOriented = positions[i].second
val rectCenterX = point.x
val rectCenterY = point.y
val left = rectCenterX - this.playerItemsWidth / 2
val top = rectCenterY - this.playerItemsHeight / 2
@ -388,11 +299,14 @@ class ReplayerAnimator(var handHistory: HandHistory, var export: Boolean) {
this.playerStackRects.add(RectF(left, top, right, bottom))
val chipCircleY = rectCenterY + chipYOffset - chipTextYOffsetCoef * chipRadius
// val chipCircleY = rectCenterY + chipYOffset - chipTextYOffsetCoef * chipRadius
val line = MathUtils.Line(point.x, point.y, tableRect.centerX(), tableRect.centerY())
val chipPoint = line.pointForDistance(this.playerItemsWidth * 0.8f)
this.chipCircles.add(Circle(rectCenterX + chipXOffset, chipCircleY, chipRadius))
this.chipCircles.add(Circle(chipPoint.x, chipPoint.y, chipRadius))
this.chipTextPoints.add(TextPoint(rectCenterX + chipXOffset, chipCircleY + 2 * chipTextSize, chipTextSize))
this.chipTextPoints.add(TextPoint(chipPoint.x, chipPoint.y + 2 * chipTextSize, chipTextSize))
// we give each text zone 1/3rd of the box height, leaving 1/3 for space
// the y given is the bottom of the text rect, giving 1/18th as the offset
@ -404,9 +318,11 @@ class ReplayerAnimator(var handHistory: HandHistory, var export: Boolean) {
this.playerStackPoints.add(TextPoint(rectCenterX, rectCenterY + this.playerItemsHeight / 3, fontSize))
this.playerActionPoints.add(TextPoint(rectCenterX, rectCenterY + this.playerItemsHeight / 9, fontSize))
val orientation = if (bottomOriented) -1 else 1
val circleOffset = playerItemsHeight * 1.75f * orientation
this.playerCircles.add(Circle(rectCenterX, rectCenterY - circleOffset, this.playerItemsHeight / 2))
val cardsUsed = this.handHistory.playerSetupForPosition(pIndex)?.cards?.size ?: maxPlayerCards
val cardsUsed = this.handHistory.playerSetupForPosition(i)?.cards?.size ?: maxPlayerCards
val cardsRectangles = mutableListOf<RectF>()
if (cardsUsed > 0) {
val offSet = (cardsUsed / 2 - 0.5f) * cardWPaddingWidth
@ -423,7 +339,142 @@ class ReplayerAnimator(var handHistory: HandHistory, var export: Boolean) {
}
}
// number of players in this order: bottom / left / top / right
// val repartition = when (playerCount) {
// 2 -> mutableListOf(1, 0, 1, 0)
// 3 -> mutableListOf(1, 1, 0, 1)
// 4 -> mutableListOf(1, 1, 1, 1)
// 5 -> mutableListOf(1, 1, 2, 1)
// 6 -> mutableListOf(2, 1, 2, 1)
// 7 -> mutableListOf(2, 1, 3, 1)
// 8 -> mutableListOf(3, 1, 3, 1)
// 9 -> mutableListOf(3, 1, 4, 1)
// 10 -> mutableListOf(4, 1, 4, 1)
// else -> throw PAIllegalStateException("can't happen")
// }
// if (repartition.sum() != playerCount) {
// throw PAIllegalStateException("Problem in the $playerCount players repartition: $repartition")
// }
// if (portrait && playerCount > 4) { repartition.reverse() } // adjustment for portrait vs landscape
//
// val chipsVerticalOffsetFromStackCenter = this.playerItemsHeight * 0.9f
// val chipsHorizontalOffsetFromStackCenter = this.tableHPadding * 1.2f
//
// var pIndex = -1
// repartition.forEachIndexed { index, count ->
//
// val xItemSpacing = width / (count + 1)
// val yItemSpacing = height / (count + 1)
//
// var rectCenterX: Float
// var rectCenterY: Float
// var chipXOffset: Float
// var chipYOffset: Float
// var chipTextYOffsetCoef = 0f
// var circleOffset = playerItemsHeight * 1.75f
//
// for (i in 1..count) {
//
// pIndex += 1
// Timber.d("Creating dimensions for player $pIndex")
//
// // when the position is in a corner, because we have 3/4 players
// // we need an additional offset direction to display the chips
// val cornerDirection = when {
// count > 2 && i == 1 -> { 1 }
// count > 2 && i == count -> { - 1 }
// else -> { 0 }
// }
//
// when (index) {
// 0 -> { // bottom
// rectCenterX = xItemSpacing * i
// rectCenterY = height - this.tableVPadding
// circleOffset *= -1
// chipXOffset = cornerDirection * chipsHorizontalOffsetFromStackCenter
// chipYOffset = -1 * chipsVerticalOffsetFromStackCenter
// chipTextYOffsetCoef = 1f
// }
// 1 -> { // left
// rectCenterY = if (count == 4 && (i == 2 || i == 3)) {
// height - yItemSpacing - this.playerItemsHeight * 1.2f - pzHeight * (i - 2)
// } else {
// height - yItemSpacing * i
// }
// rectCenterX = this.tableHPadding
//// rectCenterY = height - yItemSpacing * i
// chipXOffset = 1 * chipsHorizontalOffsetFromStackCenter
// chipYOffset = cornerDirection * -1 * chipsVerticalOffsetFromStackCenter
// chipTextYOffsetCoef = 1f * (i - 1) / count
//
// if (count == 4 && i == 1) {
// circleOffset *= -1
// } else {
// circleOffset = abs(circleOffset)
// }
//
// }
// 2 -> { // top
// rectCenterX = xItemSpacing * i
// rectCenterY = this.tableVPadding
// chipXOffset = cornerDirection * -1 * chipsHorizontalOffsetFromStackCenter
// chipYOffset = 1 * chipsVerticalOffsetFromStackCenter
// }
// 3 -> { // right
// rectCenterX = width - this.tableHPadding
// rectCenterY = yItemSpacing * i
// chipXOffset = -1 * chipsHorizontalOffsetFromStackCenter
// chipYOffset = cornerDirection * chipsVerticalOffsetFromStackCenter
// chipTextYOffsetCoef = 1f * (count - i + 1) / count
// }
// else -> throw PAIllegalStateException("can't happen")
// }
//
// val left = rectCenterX - this.playerItemsWidth / 2
// val top = rectCenterY - this.playerItemsHeight / 2
// val right = rectCenterX + this.playerItemsWidth / 2
// val bottom = rectCenterY + this.playerItemsHeight / 2
// this.playerStackRects.add(RectF(left, top, right, bottom))
//
//
// val chipCircleY = rectCenterY + chipYOffset - chipTextYOffsetCoef * chipRadius
//
// this.chipCircles.add(Circle(rectCenterX + chipXOffset, chipCircleY, chipRadius))
//
// this.chipTextPoints.add(TextPoint(rectCenterX + chipXOffset, chipCircleY + 2 * chipTextSize, chipTextSize))
//
// // we give each text zone 1/3rd of the box height, leaving 1/3 for space
// // the y given is the bottom of the text rect, giving 1/18th as the offset
// // 1 / (3_total_space * 3_each_space * 2_center)
//
// val bottomOffset = this.playerItemsHeight / (3 * 3 * 2)
// val fontSize = this.playerItemsHeight / 3
// this.playerNamePoints.add(TextPoint(rectCenterX, rectCenterY - bottomOffset, fontSize))
// this.playerStackPoints.add(TextPoint(rectCenterX, rectCenterY + this.playerItemsHeight / 3, fontSize))
// this.playerActionPoints.add(TextPoint(rectCenterX, rectCenterY + this.playerItemsHeight / 9, fontSize))
//
// this.playerCircles.add(Circle(rectCenterX, rectCenterY - circleOffset, this.playerItemsHeight / 2))
//
// val cardsUsed = this.handHistory.playerSetupForPosition(pIndex)?.cards?.size ?: maxPlayerCards
// val cardsRectangles = mutableListOf<RectF>()
// if (cardsUsed > 0) {
// val offSet = (cardsUsed / 2 - 0.5f) * cardWPaddingWidth
//
// val cardCenterY = rectCenterY - circleOffset / 2
// for (c in 0 until cardsUsed) {
//
// val cardCenterX = rectCenterX - offSet + c * cardWPaddingWidth
// val cardRect = RectF(cardCenterX - cardSpecs.width / 2, cardCenterY - cardSpecs.height / 2, cardCenterX + cardSpecs.width / 2, cardCenterY + cardSpecs.height / 2)
// cardsRectangles.add(cardRect)
// }
// }
// this.playerCardRects.add(cardsRectangles)
//
// }
//
// }
}

@ -48,6 +48,7 @@ class TableDrawer(bitmap: Bitmap) : Canvas(bitmap) {
private val fillPaint = Paint()
private val tablePaint = Paint()
private val feltPaint = Paint()
private val textPaint = Paint()
private val cardTextPaint = Paint()
private val cardStrokePaint = Paint()
@ -62,9 +63,14 @@ class TableDrawer(bitmap: Bitmap) : Canvas(bitmap) {
backgroundPaint.color = context.getColor(backgroundColor)
tablePaint.isAntiAlias = true
tablePaint.style = Paint.Style.STROKE
tablePaint.strokeWidth = animator.tableStrokeWidth
tablePaint.color = context.getColor(R.color.green)
tablePaint.style = Paint.Style.STROKE
feltPaint.isAntiAlias = true
feltPaint.color = context.getColor(R.color.green_transparent_felt)
feltPaint.style = Paint.Style.STROKE
feltPaint.strokeWidth = animator.tableStrokeWidth
strokePaint.isAntiAlias = true
strokePaint.style = Paint.Style.STROKE
@ -212,6 +218,27 @@ class TableDrawer(bitmap: Bitmap) : Canvas(bitmap) {
canvas.drawColor(context.getColor(backgroundColor))
// Felt rosace
val xr = animator.tableCornerXRadius
val yr = animator.tableCornerYRadius
val r = animator.tableRect
val r1 = RectF(r.left, r.top, r.left + 2 * xr, r.top + 2 * yr)
canvas.drawRoundRect(r1, animator.tableCornerXRadius, animator.tableCornerYRadius, this.feltPaint)
val r2 = RectF(r.right - 2 * xr, r.top, r.right, r.top + 2 * yr)
canvas.drawRoundRect(r2, animator.tableCornerXRadius, animator.tableCornerYRadius, this.feltPaint)
val r3 = RectF(r.right - 2 * xr, r.bottom - 2 * yr, r.right, r.bottom)
canvas.drawRoundRect(r3, animator.tableCornerXRadius, animator.tableCornerYRadius, this.feltPaint)
val r4 = RectF(r.left, r.bottom - 2 * yr, r.left + 2 * xr, r.bottom)
canvas.drawRoundRect(r4, animator.tableCornerXRadius, animator.tableCornerYRadius, this.feltPaint)
// val r5 = RectF(r.right - 2 * xr, r.centerY() - yr, r.right, r.centerY() + yr)
// canvas.drawRoundRect(r5, animator.tableCornerXRadius, animator.tableCornerYRadius, this.feltPaint)
// val r6 = RectF(r.right - 2 * xr, r.top, r.right, r.top + 2 * yr)
// canvas.drawRoundRect(r6, animator.tableCornerXRadius, animator.tableCornerYRadius, this.feltPaint)
// val r7 = RectF(r.left, r.centerY() - yr, r.left + 2 * xr, r.centerY() + yr)
// canvas.drawRoundRect(r7, animator.tableCornerXRadius, animator.tableCornerYRadius, this.feltPaint)
// val r8 = RectF(r.left, r.bottom - 2 * yr, r.left + 2 * xr, r.bottom)
// canvas.drawRoundRect(r8, animator.tableCornerXRadius, animator.tableCornerYRadius, this.feltPaint)
canvas.drawRoundRect(animator.tableRect, animator.tableCornerXRadius, animator.tableCornerYRadius, this.tablePaint)
this.cardTextPaint.textSize = animator.cardSpecs.height * .38f

@ -0,0 +1,112 @@
package net.pokeranalytics.android.util
import android.graphics.PointF
import android.graphics.RectF
import kotlin.math.pow
import kotlin.math.sqrt
import kotlin.math.tan
class MathUtils {
private interface Path {
val length: Float
fun pointForDistance(distance: Float): PointF
}
class Line(val x1: Float, val y1: Float, val x2: Float, val y2: Float): Path {
override val length: Float
get() {
return PointF(x2 - x1, y2 - y1).length()
}
override fun pointForDistance(distance: Float): PointF {
val ratio = distance / this.length
return PointF(x1 + ratio * (x2 - x1), y1 + ratio * (y2 - y1))
}
val slope: Float
get() { return (x2 - x1) / (y2 - y1) }
}
private class EllipseQuarter(val x1: Float, val y1: Float, val xRadius: Float, val yRadius: Float, val direction: Direction): Path {
enum class Direction { SOUTH_EAST, SOUTH_WEST, NORTH_EAST, NORTH_WEST;
val east: Boolean
get() { return this == SOUTH_EAST || this == NORTH_EAST }
val angle: Float
get() {
return when (this) {
NORTH_EAST -> Math.PI.toFloat() * 0.5f
NORTH_WEST -> Math.PI.toFloat()
SOUTH_EAST -> 0f
SOUTH_WEST -> Math.PI.toFloat() * 1.5f
}
}
}
override val length: Float
get() {
return ellipseCircumference(this.xRadius, this.yRadius) / 4f
}
override fun pointForDistance(distance: Float): PointF {
val angle = Math.PI.toFloat() / 2 * distance / length
return pointForAngle(angle)
}
private fun pointForAngle(angle: Float): PointF {
val baseAngle = direction.angle - angle
val denominator = sqrt(yRadius.pow(2) + xRadius.pow(2) * tan(baseAngle).pow(2))
val x = xRadius * yRadius / denominator
val y = xRadius * yRadius * tan(baseAngle) / denominator
return if (this.direction.east) PointF(x1 + x, y1 - y)
else PointF(x1 - x, y1 + y)
}
}
companion object{
private fun ellipseCircumference(a: Float, b: Float): Float {
return Math.PI.toFloat() * sqrt(2f * (a.pow(2) + b.pow(2)))
}
fun positionsAroundRoundedRectangle(numberOfPlayers: Int, rect: RectF, xRadius: Float, yRadius: Float): List<Pair<PointF, Boolean>> {
val segments = listOf(
Line(rect.centerX(), rect.bottom, rect.left + xRadius, rect.bottom),
EllipseQuarter(rect.left + xRadius, rect.bottom - yRadius, xRadius, yRadius, EllipseQuarter.Direction.SOUTH_WEST),
Line(rect.left, rect.bottom - yRadius, rect.left, rect.top + yRadius),
EllipseQuarter(rect.left + xRadius, rect.top + yRadius, xRadius, yRadius, EllipseQuarter.Direction.NORTH_WEST),
Line(rect.left + xRadius, rect.top, rect.right - xRadius, rect.top),
EllipseQuarter(rect.right - xRadius, rect.top + yRadius, xRadius, yRadius, EllipseQuarter.Direction.NORTH_EAST),
Line(rect.right, rect.top + yRadius, rect.right, rect.bottom - yRadius),
EllipseQuarter(rect.right - xRadius, rect.bottom - yRadius, xRadius, yRadius, EllipseQuarter.Direction.SOUTH_EAST),
Line(rect.right - xRadius, rect.bottom, rect.centerX(), rect.bottom)
)
val perimeter = segments.sumByDouble { it.length.toDouble() }
val distancePerPlayer = perimeter / numberOfPlayers
val playerPoints = mutableListOf<Pair<PointF, Boolean>>()
for (i in 0 until numberOfPlayers) {
var distanceFromOrigin = i * distancePerPlayer.toFloat()
var pathIndex = 0
while (distanceFromOrigin > segments[pathIndex].length) {
distanceFromOrigin -= segments[pathIndex].length
pathIndex++
}
val p = segments[pathIndex].pointForDistance(distanceFromOrigin)
// 2 first and 2 last are at the bottom
val bottom = pathIndex < 2 || pathIndex > 6
playerPoints.add(Pair(p, bottom))
}
return playerPoints
}
}
}

@ -21,6 +21,7 @@
<color name="green">#58C473</color>
<color name="green_transparent">#2558C473</color>
<color name="green_transparent_felt">#257BC18C</color>
<color name="green_light">#65FF82</color>
<color name="green_lighter_gradient">#282e29</color>
<color name="green_diamond_dark">#2E8148</color>

Loading…
Cancel
Save