comments pt1

This commit is contained in:
2026-09-07 19:06:10 +08:00
parent de45eab0f5
commit 39e6291e9d
4 changed files with 92 additions and 3 deletions
@@ -369,6 +369,15 @@ class BowlingCameraActivity : AppCompatActivity(), CameraXController.Callback {
debugSessionLogger.stop()
}
/**
* @brief Advances the step index and updates the step button label.
*
* This method increments the current step index, cycling back to zero
* once the end of the [stepLabels] list is reached. It then updates the
* `btnShowStep` text to reflect the new step, ensuring the UI button
* always displays the correct label for the current position in the
* sequence.
*/
fun onStepIncrease() {
currentStepIndex = (currentStepIndex + 1) % stepLabels.size
binding.btnShowStep.setText(stepLabels[currentStepIndex])
@@ -39,6 +39,8 @@ import com.example.jnicpp.R
import java.text.SimpleDateFormat
import java.util.Locale
import com.google.mlkit.vision.pose.PoseLandmark // for testing
/**
* @brief Owns all CameraX use-case binding and recording control.
*
@@ -271,7 +273,18 @@ class CameraXController(
mirror = frame.isMirroring
)
PoseSkeletonRenderer.draw(canvas, poseFrame.landmarks, transform, overlayBonePaint, overlayJointPaint)
feedbackUI?.drawCircles(canvas, poseFrame.landmarks, transform, true)
// currentLandmarks to change to landmarks that require highlighting
// test code to contain only left wrist in currentLandmarks to not clutter the screen
val leftWrist = poseFrame.landmarks[PoseLandmark.LEFT_WRIST]
// Build a singleitem map if it exists
val singleLandmark = if (leftWrist != null) {
mapOf(PoseLandmark.LEFT_WRIST to leftWrist)
} else {
emptyMap()
}
feedbackUI?.drawCircles(canvas, singleLandmark, transform, true)
feedbackUI?.showBanner(canvas, "Body too upright, take a larger 1st step", true)
}
true
@@ -62,6 +62,18 @@ class FeedbackUI(private val context: Context, private val rootView: View) {
}
}
/**
* @brief Displays a banner with the given message on the provided canvas.
*
* The banner is centered horizontally and offset vertically. It uses
* `StaticLayout` to support multi-line text and scales its size depending
* on whether the canvas is for live UI or recording output.
*
* @param canvas The canvas to draw the banner on.
* @param message The text message to display inside the banner.
* @param forRecord If true, scales the banner relative to recording canvas
* dimensions; otherwise uses live UI scale.
*/
fun showBanner(canvas: Canvas, message: String, forRecord: Boolean = false) {
val scale = if (forRecord) computeCamScale(canvas) else 1f
val paddingX = bannerPaddingX * scale
@@ -104,6 +116,19 @@ class FeedbackUI(private val context: Context, private val rootView: View) {
canvas.restore()
}
/**
* @brief Draws glowing circles around all provided landmarks.
*
* Each landmarks coordinates are transformed by the given matrix before
* drawing. Circles are rendered with both a white stroke and a red glow
* effect for visibility.
*
* @param canvas The canvas to draw circles on.
* @param landmarks A map of landmark indices to smoothed landmark positions.
* @param transform A matrix applied to landmark coordinates before drawing.
* @param forRecord If true, scales circle radius and stroke width for
* recording output.
*/
// --- Shape overlay (circle) ---
fun drawCircles(canvas: Canvas, landmarks: Map<Int, SmoothedLandmark>, transform: Matrix, forRecord: Boolean = false) {
for (landmark in landmarks.values) {
@@ -113,6 +138,22 @@ class FeedbackUI(private val context: Context, private val rootView: View) {
}
}
/**
* @brief Draws a single circle with both a solid stroke and a glowing outline.
*
* This method renders a circle at the specified coordinates using two
* layered paints: a white stroke (`circlePaint`) and a red glow (`glowPaint`).
* The radius and stroke widths are scaled depending on whether the canvas
* is for live UI or recording output, ensuring consistent visual feedback
* across different resolutions.
*
* @param canvas The canvas to draw the circle on.
* @param x The xcoordinate of the circles center.
* @param y The ycoordinate of the circles center.
* @param radius The circle radius. If set to 0, defaults to [CIRCLE_RADIUS].
* @param forRecord If true, applies recording scale factor to radius and
* stroke width; otherwise uses live UI scale.
*/
fun drawCircle(canvas: Canvas, x: Float, y: Float, radius: Float = 0f, forRecord: Boolean = false) {
val scale = if (forRecord) camScale else 1f
@@ -123,13 +164,29 @@ class FeedbackUI(private val context: Context, private val rootView: View) {
canvas.drawCircle(x, y, rad, glowPaint)
}
// Store live UI size once
/**
* @brief Updates stored live UI dimensions based on the root view.
*
* This method caches the width and height of the root view so that
* recording canvas scaling can be computed consistently later.
*
* @param rootView The root view whose dimensions are measured.
*/
fun setLiveUiSize(rootView: View) {
liveUiWidth = rootView.width
liveUiHeight = rootView.height
}
// Compute scale when drawing to recording canvas
/**
* @brief Computes the scaling factor between live UI and recording canvas.
*
* The scale is determined by comparing the recording canvas dimensions
* against the cached live UI dimensions, using the smaller ratio to
* preserve aspect consistency.
*
* @param recordCanvas The canvas used for recording output.
* @return A float scale factor to apply when drawing to the recording canvas.
*/
private fun computeCamScale(recordCanvas: Canvas): Float {
if (liveUiWidth == 0 || liveUiHeight == 0) return 1f
val scaleX = recordCanvas.width.toFloat() / liveUiWidth.toFloat()
@@ -144,6 +144,16 @@ class PoseOverlayView @JvmOverloads constructor(
feedbackUI?.showBanner(canvas, "Body too upright, take a larger 1st step efsdfdg d dg df gdgdfg df ")
}
/**
* @brief Attaches a FeedbackUI instance to this component.
*
* This method stores a reference to the provided [FeedbackUI] so that
* banner rendering and landmark overlays can be delegated to it. By
* attaching the UI handler here, the parent component gains access to
* feedback drawing utilities without needing to manage them directly.
*
* @param feedbackUI The [FeedbackUI] instance to associate with this component.
*/
fun attachFeedback(feedbackUI: FeedbackUI) {
this.feedbackUI = feedbackUI
}