import { Component, Input, OnChanges } from '@angular/core'; import { AnnotatedMove } from '../../models/analysis.models'; interface TimelinePoint { x: number; y: number; eval: number; san: string; plyIndex: number; } const CLAMP = 5; // clamp eval to ±5 pawns for display const HEIGHT = 80; const WIDTH = 600; @Component({ selector: 'app-eval-timeline', standalone: true, imports: [], templateUrl: './eval-timeline.component.html', styleUrl: './eval-timeline.component.css', }) export class EvalTimelineComponent implements OnChanges { @Input({ required: true }) moves: AnnotatedMove[] = []; @Input() activePly: number | null = null; points: TimelinePoint[] = []; evalPolyline = ''; polylineWhite = ''; polylineBlack = ''; svgWidth = WIDTH; svgHeight = HEIGHT; ngOnChanges(): void { this.buildChart(); } activeX(): number | null { if (this.activePly === null) return null; const pt = this.points[this.activePly]; return pt ? pt.x : null; } private buildChart(): void { if (this.moves.length === 0) { this.points = []; this.evalPolyline = ''; this.polylineWhite = ''; this.polylineBlack = ''; return; } const total = this.moves.length; this.points = this.moves.map((m, i) => { const evalValue = m.evalAfter ?? 0; const clamped = Math.max(-CLAMP, Math.min(CLAMP, evalValue)); const x = (i / Math.max(total - 1, 1)) * WIDTH; // y=0 => white winning (top), y=HEIGHT => black winning (bottom) const y = ((CLAMP - clamped) / (CLAMP * 2)) * HEIGHT; return { x, y, eval: evalValue, san: m.san, plyIndex: i }; }); const coordStr = this.points.map((p) => `${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(' '); this.evalPolyline = coordStr; const mid = HEIGHT / 2; const first = this.points[0]; const last = this.points[this.points.length - 1]; this.polylineWhite = `${first.x.toFixed(1)},${mid} ${coordStr} ${last.x.toFixed(1)},${mid}`; this.polylineBlack = this.polylineWhite; } }