ux: Sensor Alignment — Step 0/N, arrow↔hint sync, arrow 위치, oval 색
1) 시작 전 (waitingForStart) Step 표기: "Step 1/3" → "Step 0/3" 으로 표시, 점/체크포인트 row 전체 숨김 (시작 후에만 점 등장). 2) 화살표 ↔ Hint 2 텍스트 mismatch 수정 (V1 path): debounce 중 else 분기에서 directionIcon 을 guideResult.icon 으로 즉시 갱신 → 텍스트는 "위로 올려" 인데 화살표는 가끔 아래를 가리키는 깜빡임 발생. 수정: directionHint 텍스트에서 방향 추론 (phaseHoldHint 와 동일 로직) → 화면에 보이는 텍스트와 항상 일관. V2 path 는 이미 동일 source(st.action) 라 영향 없음. 3) 화살표 위치 — 센서/방광 쪽으로 당김: vertCloser = arrowDrawH * 0.45f 도입. - 위 화살표 cya: 기존 + vertCloser (살짝 아래로) - 아래 화살표 cya: 기존 - vertCloser (살짝 위로) 좌/우 화살표는 변경 없음. 4) 방광 oval 색 (센서 주위 테두리) — 미정렬 상태: Cyan → Color(0xFFFFB74D) (Material orange 300, warm hint). greenZone 진입 시는 기존 Green 4CAF50 유지. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+36
-22
@@ -362,7 +362,8 @@ fun PlacementGuideView(appState: AppState) {
|
|||||||
val bladderCy = navelY + (pubisY - navelY) * 0.75f
|
val bladderCy = navelY + (pubisY - navelY) * 0.75f
|
||||||
val bladderRx = w * 0.18f
|
val bladderRx = w * 0.18f
|
||||||
val bladderRy = bladderRx * 0.70f
|
val bladderRy = bladderRx * 0.70f
|
||||||
val ovalColor = if (greenZone) Color(0xFF4CAF50) else Color.Cyan
|
// In-position 아닐 때는 연한 주황 (warm hint), 완료되면 기존 그린.
|
||||||
|
val ovalColor = if (greenZone) Color(0xFF4CAF50) else Color(0xFFFFB74D)
|
||||||
drawOval(
|
drawOval(
|
||||||
color = ovalColor.copy(alpha = 0.12f),
|
color = ovalColor.copy(alpha = 0.12f),
|
||||||
topLeft = Offset(cx - bladderRx, bladderCy - bladderRy),
|
topLeft = Offset(cx - bladderRx, bladderCy - bladderRy),
|
||||||
@@ -403,18 +404,20 @@ fun PlacementGuideView(appState: AppState) {
|
|||||||
val arrowDrawH = arrowDrawW * (arrowBitmap.height.toFloat() / arrowBitmap.width.toFloat())
|
val arrowDrawH = arrowDrawW * (arrowBitmap.height.toFloat() / arrowBitmap.width.toFloat())
|
||||||
val sensorHalfH = h * 0.08f
|
val sensorHalfH = h * 0.08f
|
||||||
val gap = 20f
|
val gap = 20f
|
||||||
|
// 위/아래 화살표를 센서/방광 쪽으로 가깝게 당김 (사용자 요구).
|
||||||
|
val vertCloser = arrowDrawH * 0.45f
|
||||||
|
|
||||||
// 화살표 center 위치 + 회전 각도 (icon 이 우향이므로 right=0°)
|
// 화살표 center 위치 + 회전 각도 (icon 이 우향이므로 right=0°)
|
||||||
data class ArrowPlacement(val cxa: Float, val cya: Float, val rot: Float)
|
data class ArrowPlacement(val cxa: Float, val cya: Float, val rot: Float)
|
||||||
val placement: ArrowPlacement? = when {
|
val placement: ArrowPlacement? = when {
|
||||||
directionIcon.contains("up") -> ArrowPlacement(
|
directionIcon.contains("up") -> ArrowPlacement(
|
||||||
cx,
|
cx,
|
||||||
bladderCy - bladderRy - sensorHalfH - gap - arrowDrawH / 2f,
|
bladderCy - bladderRy - sensorHalfH - gap - arrowDrawH / 2f + vertCloser,
|
||||||
-90f,
|
-90f,
|
||||||
)
|
)
|
||||||
directionIcon.contains("down") -> ArrowPlacement(
|
directionIcon.contains("down") -> ArrowPlacement(
|
||||||
cx,
|
cx,
|
||||||
bladderCy + bladderRy + sensorHalfH + gap + arrowDrawH / 2f,
|
bladderCy + bladderRy + sensorHalfH + gap + arrowDrawH / 2f - vertCloser,
|
||||||
90f,
|
90f,
|
||||||
)
|
)
|
||||||
directionIcon.contains("left") -> ArrowPlacement(
|
directionIcon.contains("left") -> ArrowPlacement(
|
||||||
@@ -482,27 +485,31 @@ fun PlacementGuideView(appState: AppState) {
|
|||||||
}
|
}
|
||||||
idx to 3
|
idx to 3
|
||||||
}
|
}
|
||||||
|
// waitingForStart 일 때는 "Step 0/N" + 모든 점 숨김 (체크포인트 포함).
|
||||||
|
val displayStepIdx = if (waitingForStart) -1 else currentStepIdx
|
||||||
Text(
|
Text(
|
||||||
text = "Step ${currentStepIdx + 1}/$totalSteps",
|
text = "Step ${displayStepIdx + 1}/$totalSteps",
|
||||||
fontSize = 28.sp, fontWeight = FontWeight.Bold, color = Color.Black
|
fontSize = 28.sp, fontWeight = FontWeight.Bold, color = Color.Black
|
||||||
)
|
)
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp),
|
if (!waitingForStart) {
|
||||||
verticalAlignment = Alignment.CenterVertically) {
|
Row(horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
repeat(totalSteps) { i ->
|
verticalAlignment = Alignment.CenterVertically) {
|
||||||
val isDone = i < currentStepIdx
|
repeat(totalSteps) { i ->
|
||||||
val isCurrent = i == currentStepIdx
|
val isDone = i < currentStepIdx
|
||||||
val dotSize = if (isCurrent) 16.dp else 10.dp // 12/8 → 16/10
|
val isCurrent = i == currentStepIdx
|
||||||
val dotColor = when {
|
val dotSize = if (isCurrent) 16.dp else 10.dp
|
||||||
isCurrent -> MlPrimary
|
val dotColor = when {
|
||||||
isDone -> MlPrimary.copy(alpha = 0.6f)
|
isCurrent -> MlPrimary
|
||||||
else -> Color.Gray.copy(alpha = 0.3f)
|
isDone -> MlPrimary.copy(alpha = 0.6f)
|
||||||
|
else -> Color.Gray.copy(alpha = 0.3f)
|
||||||
|
}
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(dotSize)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(dotColor)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.size(dotSize)
|
|
||||||
.clip(CircleShape)
|
|
||||||
.background(dotColor)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isSweepMode) {
|
if (isSweepMode) {
|
||||||
@@ -881,8 +888,15 @@ fun PlacementGuideView(appState: AppState) {
|
|||||||
else -> "checkmark.circle"
|
else -> "checkmark.circle"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 디바운스 중이라도 화살표 방향은 최신 상태로 갱신
|
// 디바운스 중 — 화살표를 hint 텍스트에서 추론해 텍스트와 일관 유지.
|
||||||
directionIcon = guideResult.icon
|
// (이전: guideResult.icon 즉시 갱신 → "위로 올려" 메세지에 아래 화살표 깜빡임)
|
||||||
|
directionIcon = when {
|
||||||
|
directionHint.contains("up", ignoreCase = true) || directionHint.contains("↑") -> "arrow.up"
|
||||||
|
directionHint.contains("down", ignoreCase = true) || directionHint.contains("↓") -> "arrow.down"
|
||||||
|
directionHint.contains("left", ignoreCase = true) || directionHint.contains("←") -> "arrow.left"
|
||||||
|
directionHint.contains("right", ignoreCase = true) || directionHint.contains("→") -> "arrow.right"
|
||||||
|
else -> directionIcon // 유지
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GREEN hold 중이면 힌트 강제 유지
|
// GREEN hold 중이면 힌트 강제 유지
|
||||||
|
|||||||
Reference in New Issue
Block a user