feat(alignment): Option A — V2 활성 시 graph detection 도 method_d 결과 사용 + 2-column 그래프 + Y축 1.5×

Option A:
- V2 활성 시 channelDetected 를 method_d 의 summary.detected 로 덮어씀
- PiezoEchoAnalyzer (wall pair 양쪽 필요) ↔ method_d (low-echo span만) 의 결과 차이 해소
- ch3 같은 한쪽 벽 약한 채널이 "메시지=검출 / 그래프=미검출" 이던 시각 모순 해결

UI:
- 6개 echo waveform 세로 적층 → 2-column (좌: ch0/1/2/3 머리→치골, 우: ch4/5 좌·우 lateral)
- Y축 그래프 높이 80dp → 120dp (1.5×) 로 peak 가시성 ↑

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-26 10:37:34 +09:00
parent 12a9089102
commit 122d9631f3
@@ -668,12 +668,12 @@ fun PlacementGuideView(appState: AppState) {
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
Text("Echo Waveforms", fontSize = 13.sp, fontWeight = FontWeight.Bold, color = MlPrimary)
val graphMethod = com.example.medilightv2android.managers.GreenZoneConstants.detectionMethod
val graphAnalyzerA = com.example.medilightv2android.managers.PiezoEchoAnalyzerA.shared
for (ch in 0 until 6) {
// 채널별 그래프 항목 — 좌/우 column 에 4개/2개씩 분배 (해부학적 위치 직관).
// Y 축 높이 80→120 (1.5×) 로 peak 잘 보이게.
@Composable
fun ChannelGraphItem(ch: Int) {
val chData = lastChannelData.find { it.channel == ch }
val detected = if (ch < channelDetected.size) channelDetected[ch] else false
// 그래프 벽 표시: lastWallResults에서 가져옴 (선택된 Method 결과와 동일)
val wallPair = if (ch < lastWallResults.size) lastWallResults[ch] else null
val showWalls = if (wallPair != null) {
com.example.medilightv2android.managers.LowEchoResult(
@@ -706,17 +706,30 @@ fun PlacementGuideView(appState: AppState) {
analyzer = analyzer,
antIndex = showWalls?.ant,
postIndex = showWalls?.post,
modifier = Modifier.fillMaxWidth().height(80.dp)
modifier = Modifier.fillMaxWidth().height(120.dp)
)
} else {
Box(modifier = Modifier.fillMaxWidth().height(80.dp).background(Color.Gray.copy(alpha = 0.05f), RoundedCornerShape(4.dp)),
Box(modifier = Modifier.fillMaxWidth().height(120.dp).background(Color.Gray.copy(alpha = 0.05f), RoundedCornerShape(4.dp)),
contentAlignment = Alignment.Center) {
Text("No data", fontSize = 10.sp, color = MlSecondaryText.copy(alpha = 0.5f))
}
}
}
if (ch < 5) {
HorizontalDivider(color = Color.Gray.copy(alpha = 0.2f))
}
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
// 좌: 0/1/2/3 (머리 → 치골 방향)
Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(6.dp)) {
for (ch in 0..3) {
ChannelGraphItem(ch)
if (ch < 3) HorizontalDivider(color = Color.Gray.copy(alpha = 0.2f))
}
}
// 우: 4/5 (좌·우 lateral)
Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(6.dp)) {
for (ch in 4..5) {
ChannelGraphItem(ch)
if (ch < 5) HorizontalDivider(color = Color.Gray.copy(alpha = 0.2f))
}
}
}
}
@@ -857,6 +870,14 @@ fun PlacementGuideView(appState: AppState) {
else DoubleArray(100)
}
val st = v2Aligner.push(raw6)
// Option A — UI graph detection 도 method_d 결과로 통일 (V2 활성 시).
// PiezoEchoAnalyzer 는 wall pair (ant+post) 둘 다 필요해 한쪽 약하면 미검출.
// method_d 는 low-echo span 만 봐서 검출. 알고리즘 ↔ 시각 일치를 위해 덮어씀.
st.summary?.detected?.let { md ->
for (i in 0 until 6) {
if (i < md.size) channelDetected[i] = md[i]
}
}
bleManager.debugLogger.info("ALIGN_V2 phase=${v2Aligner.phase} state=${st.state} action=${st.action} u4=${st.summary?.u4} u5=${st.summary?.u5} ch3=${st.summary?.ch3} bestSet=${v2Aligner.bestCenterSet}")
if (st.state == "accum") {
directionHint = st.msg