/** * Abstrakte Zeichenflaeche. * * Der Signalzeitenplan wird genau einmal beschrieben (siehe signalPlanDrawing.ts) * und auf zwei Flaechen ausgegeben: auf die Bildschirmleinwand und in das PDF. * Der Altbestand hatte zwei getrennte Zeichenroutinen; sie liefen auseinander, * sodass der Ausdruck andere Signalbilder zeigte als der Bildschirm. * * Alle Koordinaten in Punkten mit Ursprung links oben. */ export interface TextStyle { readonly size: number; readonly bold?: boolean; readonly color?: string; readonly align?: 'links' | 'mitte' | 'rechts'; readonly baseline?: 'oben' | 'mitte' | 'unten'; } export interface Surface { /** Nutzbare Breite der Flaeche. */ readonly width: number; /** Nutzbare Hoehe der Flaeche. */ readonly height: number; rect(x: number, y: number, width: number, height: number, fill: string): void; /** * Gefuelltes Vieleck - fuer das Band des Zeit-Weg-Diagramms. * * WARUM DIE FLAECHE UND NICHT NUR ZWEI LINIEN: Das Band einer Gruenen Welle * ist ein Parallelogramm ueber Zeit und Weg; seine AUSDEHNUNG ist die * Aussage. Zwei Randlinien zeigen zwei Fahrten, nicht den Bereich dazwischen, * und ein schraeges Band laesst sich mit `rect` nicht bilden. * * Weniger als drei Punkte zeichnen nichts: Eine Flaeche aus zwei Punkten hat * keinen Inhalt, und eine Umrandung waere hier die falsche Ersatzhandlung. */ polygon(points: readonly { readonly x: number; readonly y: number }[], fill: string): void; strokeRect( x: number, y: number, width: number, height: number, color: string, lineWidth?: number, ): void; line(x1: number, y1: number, x2: number, y2: number, color: string, lineWidth?: number): void; /** Gestrichelte Linie fuer Hilfslinien. */ dashedLine( x1: number, y1: number, x2: number, y2: number, color: string, lineWidth?: number, ): void; /** * Kreis mit Fuellung und Umriss - fuer den Signalgeber auf dem Lageplanblatt. * * BEIDE ZUGLEICH und nicht zwei Befehle: Ein Signalgebersinnbild ist ein * gefuellter Kreis MIT Rand; in zwei Aufrufen zerlegt, liefe es auf beiden * Flaechen mit verschiedener Rundung auseinander, und ueber dem Luftbild * saehe man den Spalt. * * `fill` darf leer bleiben - dann wird nur der Umriss gezeichnet. */ circle( x: number, y: number, radius: number, fill: string, stroke: string, lineWidth?: number, ): void; text(value: string, x: number, y: number, style: TextStyle): void; /** Breite eines Textes in der angegebenen Groesse. */ measureText(value: string, style: TextStyle): number; } /** Kuerzt einen Text so, dass er in die verfuegbare Breite passt. */ export function fitText( surface: Surface, value: string, maxWidth: number, style: TextStyle, ): string { if (surface.measureText(value, style) <= maxWidth) return value; let text = value; while (text.length > 1 && surface.measureText(`${text}…`, style) > maxWidth) { text = text.slice(0, -1); } return text.length <= 1 ? '' : `${text}…`; }