/* ===================================================================== ValenceChart — 감정 밸런스 타임라인 (SVG 라인 차트). 외부 ref의 Emotional Valence Timeline 차용 → Vignette 토큰 리스킨. · 내담자 valence = 실선(테라코타 --clay) · 상담자 baseline = 점선(세이지 --accent-bright) 범례·시간축·0선 포함. preserveAspectRatio="none" 로 컨테이너 채움. 순흑/순백 금지 — stroke 는 토큰 currentColor 계열만. ===================================================================== */ import type { ValencePoint } from "./mock"; export interface ValenceChartProps { client: ValencePoint[]; baseline: ValencePoint[]; /** 시간축 라벨 (좌→우) */ xLabels: string[]; } /** valence(-1~1) → SVG y(0~100, 위가 +1). */ function toY(v: number): number { const clamped = Math.max(-1, Math.min(1, v)); return 50 - clamped * 45; // +1 → 5, 0 → 50, -1 → 95 (상하 5% 여백) } /** 점 배열 → 부드러운 Catmull-Rom→베지어 path d. (단조 곡선, 과한 출렁임 없게) */ function smoothPath(points: ValencePoint[]): string { if (points.length === 0) return ""; const pts = points.map((p) => ({ x: p.t * 100, y: toY(p.v) })); if (pts.length === 1) return `M${pts[0].x},${pts[0].y}`; let d = `M${pts[0].x.toFixed(2)},${pts[0].y.toFixed(2)}`; for (let i = 0; i < pts.length - 1; i++) { const p0 = pts[i - 1] ?? pts[i]; const p1 = pts[i]; const p2 = pts[i + 1]; const p3 = pts[i + 2] ?? p2; // Catmull-Rom → 베지어 (tension 1/6) const c1x = p1.x + (p2.x - p0.x) / 6; const c1y = p1.y + (p2.y - p0.y) / 6; const c2x = p2.x - (p3.x - p1.x) / 6; const c2y = p2.y - (p3.y - p1.y) / 6; d += ` C${c1x.toFixed(2)},${c1y.toFixed(2)} ${c2x.toFixed(2)},${c2y.toFixed(2)} ${p2.x.toFixed(2)},${p2.y.toFixed(2)}`; } return d; } export function ValenceChart({ client, baseline, xLabels }: ValenceChartProps) { const clientPath = smoothPath(client); const baselinePath = smoothPath(baseline); return (
내담자 정서가 상담자 기준선
{/* 0선 (중립) */} {/* 상담자 baseline (점선, 세이지) */} {/* 내담자 valence (실선, 테라코타) */}
); }