Files
NowChess-Frontend/src/app/components/eval-timeline/eval-timeline.component.ts
T
2026-06-17 08:17:55 +02:00

74 lines
2.0 KiB
TypeScript

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;
}
}