diff --git a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt index 9c9e7e9..0a8923e 100644 --- a/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt +++ b/app/src/main/java/com/example/medilightv2android/ui/views/monitoring/PiezoMonitoringView.kt @@ -828,12 +828,14 @@ fun PiezoMonitoringView(appState: AppState) { if (!isMeasuring && !isAutoMeasuring && !spotCooldown && !spotInProgress) { spotInProgress = true spotCooldown = true + val savedVol = lastVolumeMl // 측정 중 화면 깜빡임 방지 spotScope.launch { val spotResults = mutableListOf() repeat(3) { measure() while (isMeasuring) { kotlinx.coroutines.delay(100) } if (lastVolumeMl > 0) spotResults.add(lastVolumeMl) + lastVolumeMl = savedVol // 중간 값 복원 kotlinx.coroutines.delay(200) } if (spotResults.size >= 2) { 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 1e5329c..cf06e9b 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 @@ -149,7 +149,7 @@ fun PlacementGuideView(appState: AppState) { .padding(horizontal = 24.dp), horizontalAlignment = Alignment.CenterHorizontally ) { - // ── 상반신 일러스트 + 센서 + 방향 가이드 ── + // ── 하복부 이미지 + 센서 + 방향 가이드 ── Box( modifier = Modifier .fillMaxWidth() @@ -157,55 +157,26 @@ fun PlacementGuideView(appState: AppState) { .padding(top = 8.dp), contentAlignment = Alignment.Center ) { + // 배경 이미지 (하복부 실루엣) + androidx.compose.foundation.Image( + painter = androidx.compose.ui.res.painterResource(com.example.medilightv2android.R.drawable.placement_body), + contentDescription = "Body", + modifier = Modifier.fillMaxSize(), + contentScale = androidx.compose.ui.layout.ContentScale.Fit, + alpha = if (isGreenZone) 0.6f else 0.9f + ) + + // 오버레이: 방광 + 센서 + 화살표 Canvas(modifier = Modifier.fillMaxSize()) { val w = size.width val h = size.height val cx = w / 2f val greenZone = isGreenZone - // ── 상반신 실루엣 ── - val skinColor = Color(0xFFFFDDB5) - val outlineColor = Color(0xFFD4A574) - - // 몸통 (위: 가슴 아래, 아래: 치골) - val bodyTop = h * 0.05f - val bodyBot = h * 0.88f - val bodyW = w * 0.38f - - // 몸통 타원형 - val bodyPath = Path().apply { - moveTo(cx - bodyW, bodyTop) - // 왼쪽 어깨~허리 곡선 - cubicTo(cx - bodyW * 1.15f, bodyTop + h * 0.15f, - cx - bodyW * 0.85f, bodyBot - h * 0.1f, - cx - bodyW * 0.7f, bodyBot) - // 아래쪽 (치골) - lineTo(cx + bodyW * 0.7f, bodyBot) - // 오른쪽 허리~어깨 - cubicTo(cx + bodyW * 0.85f, bodyBot - h * 0.1f, - cx + bodyW * 1.15f, bodyTop + h * 0.15f, - cx + bodyW, bodyTop) - close() - } - drawPath(bodyPath, skinColor) - drawPath(bodyPath, outlineColor, style = Stroke(width = 2f)) - - // 배꼽 - val navelY = h * 0.40f - drawCircle(outlineColor.copy(alpha = 0.5f), radius = 4f, center = Offset(cx, navelY)) - - // 치골 라인 - val pubisY = h * 0.82f - drawLine(outlineColor.copy(alpha = 0.4f), - Offset(cx - bodyW * 0.5f, pubisY), - Offset(cx + bodyW * 0.5f, pubisY), - strokeWidth = 1.5f, - pathEffect = PathEffect.dashPathEffect(floatArrayOf(8f, 4f))) - - // 방광 (점선 타원, 배꼽과 치골 사이) - val bladderCy = (navelY + pubisY) / 2f - val bladderRx = bodyW * 0.35f - val bladderRy = (pubisY - navelY) * 0.35f + // 방광 위치 (이미지의 배꼽~치골 사이) + val bladderCy = h * 0.58f + val bladderRx = w * 0.12f + val bladderRy = h * 0.10f drawOval( color = if (greenZone) Color(0xFF4CAF50).copy(alpha = 0.15f) else Color.Cyan.copy(alpha = 0.12f), topLeft = Offset(cx - bladderRx, bladderCy - bladderRy), @@ -218,9 +189,9 @@ fun PlacementGuideView(appState: AppState) { style = Stroke(width = 1.5f, pathEffect = PathEffect.dashPathEffect(floatArrayOf(6f, 4f))) ) - // ── 센서 기기 (둥근 사각형) ── - val sensorW = 50f - val sensorH = 70f + // 센서 기기 (둥근 사각형) + val sensorW = 48f + val sensorH = 64f val sensorY = bladderCy - sensorH / 2 val sensorColor = if (greenZone) Color(0xFF4CAF50) else MlPrimary drawRoundRect( @@ -303,19 +274,6 @@ fun PlacementGuideView(appState: AppState) { } } - // 라벨 (좌측) - Column( - modifier = Modifier - .align(Alignment.CenterStart) - .padding(start = 4.dp), - verticalArrangement = Arrangement.spacedBy(0.dp) - ) { - Text("가슴", fontSize = 9.sp, color = MlSecondaryText) - Spacer(modifier = Modifier.height(60.dp)) - Text("배꼽", fontSize = 9.sp, color = MlSecondaryText) - Spacer(modifier = Modifier.height(60.dp)) - Text("치골", fontSize = 9.sp, color = MlSecondaryText) - } } // ── 가이드 텍스트 (일러스트 아래) ── @@ -545,7 +503,11 @@ fun PlacementGuideView(appState: AppState) { val now = System.currentTimeMillis() val elapsed = now - lastHintChangeMs - if ((debounceCount >= 2 && elapsed >= 3000) || guideResult.isPass) { + // GREEN 진입/이탈은 즉시, 일반 힌트는 디바운스 + val wasGreen = isLocked + val isNowGreen = guideResult.isPass + val greenChanged = wasGreen != isNowGreen + if (greenChanged || (debounceCount >= 2 && elapsed >= 3000)) { directionHint = guideResult.hint directionIcon = guideResult.icon placementScore = guideResult.score diff --git a/app/src/main/res/drawable/placement_body.png b/app/src/main/res/drawable/placement_body.png new file mode 100644 index 0000000..1015059 Binary files /dev/null and b/app/src/main/res/drawable/placement_body.png differ