From a1d68bd007b597de19d393c0509818f295e7b70f Mon Sep 17 00:00:00 2001 From: jjangddu Date: Wed, 17 Jun 2026 17:25:20 +0900 Subject: [PATCH] =?UTF-8?q?ux:=20Sensor=20Alignment=20=E2=80=94=20Step=200?= =?UTF-8?q?/N,=20arrow=E2=86=94hint=20sync,=20arrow=20=EC=9C=84=EC=B9=98,?= =?UTF-8?q?=20oval=20=EC=83=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../ui/views/monitoring/PlacementGuideView.kt | 58 ++++++++++++------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PlacementGuideView.kt b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PlacementGuideView.kt index 1e57d40..a13fcdb 100644 --- a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PlacementGuideView.kt +++ b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PlacementGuideView.kt @@ -362,7 +362,8 @@ fun PlacementGuideView(appState: AppState) { val bladderCy = navelY + (pubisY - navelY) * 0.75f val bladderRx = w * 0.18f 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( color = ovalColor.copy(alpha = 0.12f), topLeft = Offset(cx - bladderRx, bladderCy - bladderRy), @@ -403,18 +404,20 @@ fun PlacementGuideView(appState: AppState) { val arrowDrawH = arrowDrawW * (arrowBitmap.height.toFloat() / arrowBitmap.width.toFloat()) val sensorHalfH = h * 0.08f val gap = 20f + // 위/아래 화살표를 센서/방광 쪽으로 가깝게 당김 (사용자 요구). + val vertCloser = arrowDrawH * 0.45f // 화살표 center 위치 + 회전 각도 (icon 이 우향이므로 right=0°) data class ArrowPlacement(val cxa: Float, val cya: Float, val rot: Float) val placement: ArrowPlacement? = when { directionIcon.contains("up") -> ArrowPlacement( cx, - bladderCy - bladderRy - sensorHalfH - gap - arrowDrawH / 2f, + bladderCy - bladderRy - sensorHalfH - gap - arrowDrawH / 2f + vertCloser, -90f, ) directionIcon.contains("down") -> ArrowPlacement( cx, - bladderCy + bladderRy + sensorHalfH + gap + arrowDrawH / 2f, + bladderCy + bladderRy + sensorHalfH + gap + arrowDrawH / 2f - vertCloser, 90f, ) directionIcon.contains("left") -> ArrowPlacement( @@ -482,27 +485,31 @@ fun PlacementGuideView(appState: AppState) { } idx to 3 } + // waitingForStart 일 때는 "Step 0/N" + 모든 점 숨김 (체크포인트 포함). + val displayStepIdx = if (waitingForStart) -1 else currentStepIdx Text( - text = "Step ${currentStepIdx + 1}/$totalSteps", + text = "Step ${displayStepIdx + 1}/$totalSteps", fontSize = 28.sp, fontWeight = FontWeight.Bold, color = Color.Black ) - Row(horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalAlignment = Alignment.CenterVertically) { - repeat(totalSteps) { i -> - val isDone = i < currentStepIdx - val isCurrent = i == currentStepIdx - val dotSize = if (isCurrent) 16.dp else 10.dp // 12/8 → 16/10 - val dotColor = when { - isCurrent -> MlPrimary - isDone -> MlPrimary.copy(alpha = 0.6f) - else -> Color.Gray.copy(alpha = 0.3f) + if (!waitingForStart) { + Row(horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically) { + repeat(totalSteps) { i -> + val isDone = i < currentStepIdx + val isCurrent = i == currentStepIdx + val dotSize = if (isCurrent) 16.dp else 10.dp + val dotColor = when { + isCurrent -> MlPrimary + 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) { @@ -881,8 +888,15 @@ fun PlacementGuideView(appState: AppState) { else -> "checkmark.circle" } } else { - // 디바운스 중이라도 화살표 방향은 최신 상태로 갱신 - directionIcon = guideResult.icon + // 디바운스 중 — 화살표를 hint 텍스트에서 추론해 텍스트와 일관 유지. + // (이전: 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 중이면 힌트 강제 유지