Skip to main content
Glama
line-chart.ts4.62 kB
import { ChartData, ChartResult } from '../types/index.js'; import { colorize } from '../utils/colors.js'; import { ASCII_CHARS, normalize, clamp, createGrid, gridToString, center, padLeft } from '../utils/ascii.js'; export function createLineChart(data: ChartData): ChartResult { const { data: values, labels, title, width = 60, height = 15, color = 'white' } = data; if (values.length === 0) { throw new Error('Data array cannot be empty'); } const chartWidth = width - 10; // Reserve space for y-axis labels const chartHeight = height - 2; // Reserve space for x-axis const minValue = Math.min(...values); const maxValue = Math.max(...values); const valueRange = maxValue - minValue; // Create the chart grid const grid = createGrid(width, height); // Draw y-axis labels and grid lines for (let y = 0; y < chartHeight; y++) { const value = maxValue - (y / (chartHeight - 1)) * valueRange; const label = value.toFixed(1); const labelStr = padLeft(label, 8); // Place y-axis label for (let i = 0; i < Math.min(labelStr.length, 8); i++) { if (8 - i < width) { grid[y][8 - i] = labelStr[i]; } } // Draw y-axis line if (9 < width) { grid[y][9] = y === chartHeight - 1 ? ASCII_CHARS.bottomLeft : y === 0 ? ASCII_CHARS.topLeft : ASCII_CHARS.teeRight; } // Draw horizontal grid lines (optional light lines) for (let x = 10; x < width; x++) { if (y === chartHeight - 1) { grid[y][x] = ASCII_CHARS.horizontal; } else if (y % 2 === 0) { grid[y][x] = '·'; // Light grid dots } } } // Draw x-axis labels if (labels && labels.length === values.length) { // const labelSpacing = Math.max(1, Math.floor(chartWidth / Math.min(labels.length, 8))); for (let i = 0; i < labels.length && i < 8; i++) { const x = 10 + Math.floor((i / (labels.length - 1)) * (chartWidth - 1)); const label = labels[i].substring(0, 6); // Truncate long labels if (x + label.length <= width && height - 1 >= 0) { for (let j = 0; j < label.length && x + j < width; j++) { grid[height - 1][x + j] = label[j]; } } } } // Plot the line const plotPoints: { x: number; y: number }[] = []; for (let i = 0; i < values.length; i++) { const x = values.length === 1 ? 10 + Math.floor(chartWidth / 2) : 10 + Math.floor((i / (values.length - 1)) * (chartWidth - 1)); const normalizedValue = valueRange === 0 ? 0.5 : normalize(values[i], minValue, maxValue); const y = Math.floor((1 - normalizedValue) * (chartHeight - 1)); plotPoints.push({ x: clamp(x, 10, width - 1), y: clamp(y, 0, chartHeight - 1) }); } // Draw line segments between points for (let i = 0; i < plotPoints.length; i++) { const point = plotPoints[i]; // Draw the point if (point.x < width && point.y < chartHeight) { grid[point.y][point.x] = '●'; } // Draw line to next point if (i < plotPoints.length - 1) { const nextPoint = plotPoints[i + 1]; drawLine(grid, point.x, point.y, nextPoint.x, nextPoint.y, chartWidth, chartHeight); } } // Convert grid to string and apply coloring let chart = gridToString(grid); if (color !== 'white') { chart = colorize(chart, color); } // Add title if provided if (title) { const titleLine = center(title, width); chart = titleLine + '\n' + chart; } return { chart, title, dimensions: { width, height } }; } function drawLine( grid: string[][], x1: number, y1: number, x2: number, y2: number, maxWidth: number, maxHeight: number ): void { const dx = Math.abs(x2 - x1); const dy = Math.abs(y2 - y1); const sx = x1 < x2 ? 1 : -1; const sy = y1 < y2 ? 1 : -1; let err = dx - dy; let x = x1; let y = y1; // eslint-disable-next-line no-constant-condition while (true) { // Draw line character based on direction if (x >= 10 && x < maxWidth + 10 && y >= 0 && y < maxHeight) { if (grid[y][x] === ' ' || grid[y][x] === '·') { if (dx > dy) { grid[y][x] = ASCII_CHARS.horizontal; } else if (dy > dx) { grid[y][x] = ASCII_CHARS.vertical; } else { grid[y][x] = x < x2 ? ASCII_CHARS.curveUpRight : ASCII_CHARS.curveUpLeft; } } } if (x === x2 && y === y2) break; const e2 = 2 * err; if (e2 > -dy) { err -= dy; x += sx; } if (e2 < dx) { err += dx; y += sy; } } }

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/gianlucamazza/mcp-ascii-charts'

If you have feedback or need assistance with the MCP directory API, please join our Discord server