From 39e6291e9d70906445775fec3e0366435f6423d5 Mon Sep 17 00:00:00 2001 From: chan-qy Date: Mon, 7 Sep 2026 19:06:10 +0800 Subject: [PATCH] comments pt1 --- .../jnicpp/bowling/BowlingCameraActivity.kt | 9 +++ .../jnicpp/bowling/CameraXController.kt | 15 ++++- .../com/example/jnicpp/bowling/FeedbackUI.kt | 61 ++++++++++++++++++- .../example/jnicpp/bowling/PoseOverlayView.kt | 10 +++ 4 files changed, 92 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/example/jnicpp/bowling/BowlingCameraActivity.kt b/app/src/main/java/com/example/jnicpp/bowling/BowlingCameraActivity.kt index 03e5c8d..255fab7 100644 --- a/app/src/main/java/com/example/jnicpp/bowling/BowlingCameraActivity.kt +++ b/app/src/main/java/com/example/jnicpp/bowling/BowlingCameraActivity.kt @@ -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]) diff --git a/app/src/main/java/com/example/jnicpp/bowling/CameraXController.kt b/app/src/main/java/com/example/jnicpp/bowling/CameraXController.kt index 3ae11a9..ef81585 100644 --- a/app/src/main/java/com/example/jnicpp/bowling/CameraXController.kt +++ b/app/src/main/java/com/example/jnicpp/bowling/CameraXController.kt @@ -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 single‑item 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 diff --git a/app/src/main/java/com/example/jnicpp/bowling/FeedbackUI.kt b/app/src/main/java/com/example/jnicpp/bowling/FeedbackUI.kt index ce83d54..5965672 100644 --- a/app/src/main/java/com/example/jnicpp/bowling/FeedbackUI.kt +++ b/app/src/main/java/com/example/jnicpp/bowling/FeedbackUI.kt @@ -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 landmark’s 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, 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 x‑coordinate of the circle’s center. + * @param y The y‑coordinate of the circle’s 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() diff --git a/app/src/main/java/com/example/jnicpp/bowling/PoseOverlayView.kt b/app/src/main/java/com/example/jnicpp/bowling/PoseOverlayView.kt index a34e597..a387eaa 100644 --- a/app/src/main/java/com/example/jnicpp/bowling/PoseOverlayView.kt +++ b/app/src/main/java/com/example/jnicpp/bowling/PoseOverlayView.kt @@ -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 }