fuzzyworld

haunt / commit

Keep map labels upright in orientation mode

f34f37144f842b0e27a5b0ba2f6ed9f8b0eae277 · Pavel Safronov · 2026-05-13 15:55:38 -0700 · parents 894372430fa0

Keep map labels upright in orientation mode

Added Canvas.drawOrientedText() extension: when orientation mode is active it
counter-rotates the canvas by -(northOffsetDeg - currentHeading) around the
label's own position before drawing, then restores. This means each label
appears next to its rotated icon at the correct map position, but the text
itself is always horizontal in screen space.

Applies to: grid tick labels, light point names, saved point names, "you".
The N indicator, empty-state text, and axis legend are already drawn in
screen space and are unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
 .../java/net/fuzzyworld/haunt/debug/MapView.kt     | 24 +++++++++++++++++-----
 1 file changed, 19 insertions(+), 5 deletions(-)

diff --git a/app/src/main/java/net/fuzzyworld/haunt/debug/MapView.kt b/app/src/main/java/net/fuzzyworld/haunt/debug/MapView.kt
index b7599e1..f6fbe85 100644
--- a/app/src/main/java/net/fuzzyworld/haunt/debug/MapView.kt
+++ b/app/src/main/java/net/fuzzyworld/haunt/debug/MapView.kt
@@ -233,6 +233,20 @@ class MapView @JvmOverloads constructor(
         canvas.drawCircle(cx, cy, r * 1.7f, ringPaint)
     }
 
+    // Draws text that is always upright in screen space, even when the canvas is rotated.
+    // The icon/point at (x, y) has already been placed by the canvas rotation; this
+    // counter-rotates only the text so it reads horizontally on the display.
+    private fun Canvas.drawOrientedText(text: String, x: Float, y: Float, paint: Paint) {
+        if (orientationEnabled) {
+            save()
+            rotate(currentHeading - northOffsetDeg, x, y)
+            drawText(text, x, y, paint)
+            restore()
+        } else {
+            drawText(text, x, y, paint)
+        }
+    }
+
     private fun drawNIndicator(canvas: Canvas) {
         val vcx = width / 2f
         val vcy = height / 2f
@@ -311,7 +325,7 @@ class MapView @JvmOverloads constructor(
             val wx = gi * stepX.toDouble()
             canvas.drawLine(sx(wx), 0f, sx(wx), height.toFloat(), gridPaint)
             dimLabelPaint.textAlign = Paint.Align.CENTER
-            canvas.drawText("${wx.roundToInt()}m", sx(wx), height - 6f, dimLabelPaint)
+            canvas.drawOrientedText("${wx.roundToInt()}m", sx(wx), height - 6f, dimLabelPaint)
         }
         val gyFirst = (minY / stepY).toInt()
         val gyLast  = (maxY / stepY).toInt()
@@ -319,7 +333,7 @@ class MapView @JvmOverloads constructor(
             val wy = gi * stepY.toDouble()
             canvas.drawLine(0f, sy(wy), width.toFloat(), sy(wy), gridPaint)
             dimLabelPaint.textAlign = Paint.Align.LEFT
-            canvas.drawText("${wy.roundToInt()}m", 4f, sy(wy) - 6f, dimLabelPaint)
+            canvas.drawOrientedText("${wy.roundToInt()}m", 4f, sy(wy) - 6f, dimLabelPaint)
         }
 
         // History trail — drawn first so it appears behind everything else
@@ -336,7 +350,7 @@ class MapView @JvmOverloads constructor(
             drawLightIcon(canvas, cx, cy, point.color, 26f)
             labelPaint.color = Color.BLACK
             labelPaint.textAlign = Paint.Align.LEFT
-            canvas.drawText(point.name, cx + 34f, cy + 11f, labelPaint)
+            canvas.drawOrientedText(point.name, cx + 34f, cy + 11f, labelPaint)
         }
 
         // Saved points — routers get AP icon, network devices get small ring, others get generic pin
@@ -350,7 +364,7 @@ class MapView @JvmOverloads constructor(
                 else -> drawPointIcon(canvas, cx, cy, point.color, 26f)
             }
             labelPaint.textAlign = Paint.Align.LEFT
-            canvas.drawText(point.name, cx + 34f, cy + 11f, labelPaint)
+            canvas.drawOrientedText(point.name, cx + 34f, cy + 11f, labelPaint)
         }
 
         // Current position — pulsing star
@@ -360,7 +374,7 @@ class MapView @JvmOverloads constructor(
             drawYouIcon(canvas, cx, cy, youColor, 22f)
             labelPaint.textAlign = Paint.Align.LEFT
             labelPaint.color = Color.parseColor("#444444")
-            canvas.drawText("you", cx + 46f, cy + 11f, labelPaint)
+            canvas.drawOrientedText("you", cx + 46f, cy + 11f, labelPaint)
             labelPaint.color = Color.BLACK
         }