Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update FieldUtil.kt #21

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
141 changes: 61 additions & 80 deletions src/main/kotlin/org/rowlandhall/meepmeep/core/util/FieldUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,112 +2,93 @@ package org.rowlandhall.meepmeep.core.util

import com.acmerobotics.roadrunner.geometry.Vector2d

import kotlin.math.max
import kotlin.math.min

/** Utility class for field-related calculations and conversions. */
/**
* Utility class for field-related coordinate conversions and scaling calculations.
* Assumes screen coordinates have origin (0,0) at top-left with Y increasing downward,
* and field coordinates have origin (0,0) at center with Y increasing upward.
*/
class FieldUtil {
companion object {
/** The width of the field. */
@JvmStatic
var FIELD_WIDTH = 141
/** Field dimensions in inches (width = X-axis, height = Y-axis) */
const val DEFAULT_FIELD_WIDTH = 141.0
const val DEFAULT_FIELD_HEIGHT = 143.0

/** The height of the field. */
@JvmStatic
var FIELD_HEIGHT = 143
// Mutable field dimensions for potential customization
@JvmStatic var fieldWidth = DEFAULT_FIELD_WIDTH
@JvmStatic var fieldHeight = DEFAULT_FIELD_HEIGHT

/** The width of the canvas in pixels. */
@JvmStatic
var CANVAS_WIDTH = 0.0
// Canvas dimensions should be set before using conversion methods
@JvmStatic var canvasWidth = 0.0
private set
@JvmStatic var canvasHeight = 0.0
private set

/** The height of the canvas in pixels. */
/** Initialize canvas dimensions with validation */
@JvmStatic
var CANVAS_HEIGHT = 0.0
fun setCanvasDimensions(width: Double, height: Double) {
require(width > 0 && height > 0) { "Canvas dimensions must be positive" }
canvasWidth = width
canvasHeight = height
}

/**
* Converts screen coordinates to field coordinates.
*
* @param vector2d The screen coordinates as a Vector2d object.
* @param canvasWidth The width of the canvas.
* @param canvasHeight The height of the canvas.
* @return The field coordinates as a Vector2d object.
* @param screenPoint Screen coordinates (origin at top-left)
* @return Field coordinates (origin at center, Y-up)
*/
@JvmStatic
@JvmOverloads
fun screenCoordsToFieldCoords(
vector2d: Vector2d,
canvasWidth: Double = CANVAS_WIDTH,
canvasHeight: Double = CANVAS_HEIGHT
): Vector2d {
// Mirror the Y coordinate and scale to field dimensions
return mirrorY(vector2d) / max(
canvasWidth,
canvasHeight
) * FIELD_WIDTH.toDouble() + Vector2d(-FIELD_WIDTH / 2.0, FIELD_HEIGHT / 2.0)
fun screenToField(screenPoint: Vector2d): Vector2d {
validateCanvasDimensions()

val normalizedX = screenPoint.x / canvasWidth
val normalizedY = (canvasHeight - screenPoint.y) / canvasHeight

return Vector2d(
normalizedX * fieldWidth - fieldWidth / 2,
normalizedY * fieldHeight - fieldHeight / 2
)
}

/**
* Converts field coordinates to screen coordinates.
*
* @param vector2d The field coordinates as a Vector2d object.
* @param canvasWidth The width of the canvas.
* @param canvasHeight The height of the canvas.
* @return The screen coordinates as a Vector2d object.
*/
@JvmStatic
@JvmOverloads
fun fieldCoordsToScreenCoords(
vector2d: Vector2d,
canvasWidth: Double = CANVAS_WIDTH,
canvasHeight: Double = CANVAS_HEIGHT
): Vector2d {
// Mirror the Y coordinate and scale to screen dimensions
return (mirrorY(vector2d) + Vector2d(FIELD_WIDTH / 2.0, FIELD_HEIGHT / 2.0)) * min(
canvasWidth,
canvasHeight
) / FIELD_WIDTH.toDouble()
}

/**
* Scales inches to pixels based on canvas dimensions.
*
* @param inches The measurement in inches.
* @param canvasWidth The width of the canvas.
* @param canvasHeight The height of the canvas.
* @return The measurement in pixels.
* @param fieldPoint Field coordinates (origin at center, Y-up)
* @return Screen coordinates (origin at top-left)
*/
@JvmStatic
fun scaleInchesToPixel(
inches: Double,
canvasWidth: Double = CANVAS_WIDTH,
canvasHeight: Double = CANVAS_HEIGHT
): Double {
// Scale inches to pixels based on the smaller dimension of the canvas
return inches / min(FIELD_WIDTH.toDouble(), FIELD_HEIGHT.toDouble()) * min(
canvasWidth,
canvasHeight
fun fieldToScreen(fieldPoint: Vector2d): Vector2d {
validateCanvasDimensions()

val normalizedX = (fieldPoint.x + fieldWidth / 2) / fieldWidth
val normalizedY = (fieldPoint.y + fieldHeight / 2) / fieldHeight

return Vector2d(
normalizedX * canvasWidth,
canvasHeight - (normalizedY * canvasHeight)
)
}

/**
* Mirrors the X coordinate of a vector.
*
* @param vector The vector to mirror.
* @return The mirrored vector.
* Scales inches to pixels using current field-to-screen ratio.
* Maintains aspect ratio using the same scale factor for both axes.
* @return Pixel equivalent for the given inch measurement
*/
private fun mirrorX(vector: Vector2d): Vector2d {
return Vector2d(-vector.x, vector.y)
@JvmStatic
fun inchesToPixels(inches: Double): Double {
validateCanvasDimensions()

val widthScale = canvasWidth / fieldWidth
val heightScale = canvasHeight / fieldHeight
val uniformScale = min(widthScale, heightScale)

return inches * uniformScale
}


/**
* Mirrors the Y coordinate of a vector.
*
* @param vector The vector to mirror.
* @return The mirrored vector.
*/
private fun mirrorY(vector: Vector2d): Vector2d {
return Vector2d(vector.x, -vector.y)
private fun validateCanvasDimensions() {
require(canvasWidth > 0 && canvasHeight > 0) {
"Canvas dimensions must be set using setCanvasDimensions() before conversions"
}
}
}
}