UNPKG

simple-ascii-chart

Version:
524 lines (523 loc) 25 kB
"use strict"; var __read = (this && this.__read) || function (o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.drawShift = exports.drawLine = exports.drawCustomLine = exports.drawChart = exports.drawGraph = exports.drawAxis = exports.drawYAxisEnd = exports.drawXAxisEnd = exports.drawPosition = void 0; var constants_1 = require("../constants"); var coords_1 = require("./coords"); /** * Places a symbol at a specific position on the graph. * @param {Object} params - Object containing parameters. * @param {Graph} params.graph - The graph matrix where the symbol will be drawn. * @param {number} params.scaledX - X-coordinate on the graph (scaled). * @param {number} params.scaledY - Y-coordinate on the graph (scaled). * @param {string} params.symbol - Symbol to draw on the graph. */ var drawPosition = function (_a) { var graph = _a.graph, scaledX = _a.scaledX, scaledY = _a.scaledY, symbol = _a.symbol, _b = _a.debugMode, debugMode = _b === void 0 ? false : _b; if (debugMode) { // Handle out-of-bounds for Y if (scaledY >= graph.length || scaledY < 0) { console.log("Drawing at [".concat(scaledX, ", ").concat(scaledY, "]"), 'Error: out of bounds Y', { graph: graph, scaledX: scaledX, scaledY: scaledY, }); return; } // Handle out-of-bounds for X if (scaledX >= graph[scaledY].length || scaledX < 0) { console.log("Drawing at [".concat(scaledX, ", ").concat(scaledY, "]"), 'Error: out of bounds X', { graph: graph, scaledX: scaledX, scaledY: scaledY, }); return; } } // Draw the symbol if within bounds try { graph[scaledY][scaledX] = symbol; } catch (error) { // Fail silently without logging if debugMode is false } }; exports.drawPosition = drawPosition; /** * Draws a tick mark at the end of the X-axis, handling bounds and axis center. * @param {Object} params - Configuration options for drawing the X-axis tick. * @param {boolean} params.hasPlaceToRender - True if there is enough space to render the tick. * @param {Point | [number | undefined, number | undefined]} [params.axisCenter] - Coordinates of the axis center (optional). * @param {number} params.yPos - The Y-position of the tick mark. * @param {Graph} params.graph - The graph matrix being drawn on. * @param {number} params.yShift - The Y-axis shift offset. * @param {number} params.i - The current iteration index. * @param {number} params.scaledX - The scaled X-position for rendering the tick. * @param {number} params.shift - X-axis offset to adjust tick positioning. * @param {number} params.signShift - Additional shift based on the sign of the axis. * @param {Symbols['axis']} params.axisSymbols - Symbols used for the axis rendering. * @param {string[]} params.pointXShift - Array of characters representing the X-axis labels. */ var drawXAxisEnd = function (_a) { var hasPlaceToRender = _a.hasPlaceToRender, axisCenter = _a.axisCenter, yPos = _a.yPos, graph = _a.graph, yShift = _a.yShift, i = _a.i, scaledX = _a.scaledX, shift = _a.shift, signShift = _a.signShift, axisSymbols = _a.axisSymbols, pointXShift = _a.pointXShift, debugMode = _a.debugMode; // Adjusts Y position based on render space and axis center presence var yShiftWhenOccupied = hasPlaceToRender ? -1 : 0; var yShiftWhenHasAxisCenter = axisCenter && axisCenter[1] !== undefined ? 1 : 0; var graphY = yPos + yShiftWhenOccupied + yShiftWhenHasAxisCenter; // Ensure graphY stays within valid bounds if (graphY < 0) graphY = 0; else if (graphY >= graph.length) graphY = graph.length - 1; // Adjust X position for rendering the tick var graphX = scaledX + yShift - i + 2 + shift; if (graphX < 0) graphX = 0; else if (graphX >= graph[graphY].length) graphX = graph[graphY].length - 1; // Draw the tick label (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: graphX, scaledY: graphY, symbol: pointXShift[pointXShift.length - 1 - i], }); // If it's the last tick, draw the tick mark if (pointXShift.length - 1 === i) { var xTickY = yPos + signShift; var xTickX = scaledX + yShift + 2 + shift; if (xTickY >= 0 && xTickY < graph.length && xTickX >= 0 && xTickX < graph[xTickY].length) { (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: xTickX, scaledY: xTickY, symbol: (axisSymbols === null || axisSymbols === void 0 ? void 0 : axisSymbols.x) || constants_1.AXIS.x, }); } } }; exports.drawXAxisEnd = drawXAxisEnd; /** * Draws tick marks for the Y-axis based on axis configurations and scales. * @param {Object} params - Configuration options for drawing the Y-axis ticks. * @param {Graph} params.graph - The graph matrix. * @param {number} params.scaledY - Scaled Y-coordinate. * @param {number} params.yShift - Shift applied to the Y-axis. * @param {Object} params.axis - Object defining the axis position. * @param {MaybePoint} [params.axisCenter] - Optional axis center coordinates. * @param {number} params.pointY - The actual Y-coordinate of the point. * @param {Formatter} params.transformLabel - Function to format the label for the Y-axis. * @param {Symbols['axis']} params.axisSymbols - Symbols used for drawing the axis. * @param {number[]} params.expansionX - Array of X-axis expansion factors. * @param {number[]} params.expansionY - Array of Y-axis expansion factors. * @param {number} params.plotHeight - The height of the plot. * @param {boolean} [params.showTickLabel] - If true, displays tick labels for all points. */ var drawYAxisEnd = function (_a) { var graph = _a.graph, scaledY = _a.scaledY, yShift = _a.yShift, axis = _a.axis, axisCenter = _a.axisCenter, pointY = _a.pointY, transformLabel = _a.transformLabel, axisSymbols = _a.axisSymbols, expansionX = _a.expansionX, expansionY = _a.expansionY, plotHeight = _a.plotHeight, showTickLabel = _a.showTickLabel, debugMode = _a.debugMode; if (showTickLabel) { var yMax = Math.max.apply(Math, __spreadArray([], __read(expansionY), false)); var yMin = Math.min.apply(Math, __spreadArray([], __read(expansionY), false)); var numTicks = plotHeight; var yStep = (yMax - yMin) / numTicks; // Draw ticks for each label for (var i = 0; i <= numTicks; i++) { var yValue = Math.round(yMax - i * yStep); // Ensure whole numbers var scaledYPos = ((yMax - yValue) / (yMax - yMin)) * (plotHeight - 1); var graphYPos = Math.floor(scaledYPos) + 1; // Ensure the Y position is within bounds if (graphYPos >= 0 && graphYPos < graph.length) { var pointYShift = (0, coords_1.toArray)(transformLabel(yValue, { axis: 'y', xRange: expansionX, yRange: expansionY })); for (var j = 0; j < pointYShift.length; j++) { var colIndex = axis.x + yShift - j; if (colIndex >= 0 && colIndex < graph[graphYPos].length) { (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: colIndex, scaledY: graphYPos, symbol: pointYShift[pointYShift.length - 1 - j], }); } } var tickMarkIndex = axis.x + yShift + 1; if (tickMarkIndex >= 0 && tickMarkIndex < graph[graphYPos].length) { (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: tickMarkIndex, scaledY: graphYPos, symbol: (axisSymbols === null || axisSymbols === void 0 ? void 0 : axisSymbols.y) || constants_1.AXIS.y, }); } } } return; } // Render ticks for specific data values if (graph[scaledY + 1][axis.x + yShift + 1] !== (axisSymbols === null || axisSymbols === void 0 ? void 0 : axisSymbols.y)) { var pointYShift = (0, coords_1.toArray)(transformLabel(pointY, { axis: 'y', xRange: expansionX, yRange: expansionY })); for (var i = 0; i < pointYShift.length; i++) { (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: axis.x + yShift - i, scaledY: scaledY + 1, symbol: pointYShift[pointYShift.length - 1 - i], }); } (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: axis.x + yShift + 1, scaledY: scaledY + 1, symbol: (axisSymbols === null || axisSymbols === void 0 ? void 0 : axisSymbols.y) || constants_1.AXIS.y, }); } }; exports.drawYAxisEnd = drawYAxisEnd; /** * Draws both X and Y axes on the graph according to visibility and center configurations. * @param {Object} params - Configuration options for drawing axes. * @param {Graph} params.graph - The graph matrix. * @param {boolean} [params.hideXAxis] - If true, hides the X-axis. * @param {boolean} [params.hideYAxis] - If true, hides the Y-axis. * @param {MaybePoint} [params.axisCenter] - Optional axis center coordinates. * @param {Symbols['axis']} params.axisSymbols - Symbols used for axis rendering. * @param {Object} params.axis - Object defining the axis position (x and y coordinates). */ var drawAxis = function (_a) { var graph = _a.graph, hideXAxis = _a.hideXAxis, hideYAxis = _a.hideYAxis, axisCenter = _a.axisCenter, axisSymbols = _a.axisSymbols, axis = _a.axis, debugMode = _a.debugMode; graph.forEach(function (line, index) { line.forEach(function (_, curr) { var lineChar = ''; if (curr === axis.x && !hideYAxis) { if (index === 0) { lineChar = (axisSymbols === null || axisSymbols === void 0 ? void 0 : axisSymbols.n) || constants_1.AXIS.n; } else if (index === graph.length - 1 && !axisCenter && !(hideYAxis || hideXAxis)) { lineChar = (axisSymbols === null || axisSymbols === void 0 ? void 0 : axisSymbols.nse) || constants_1.AXIS.nse; } else { lineChar = (axisSymbols === null || axisSymbols === void 0 ? void 0 : axisSymbols.ns) || constants_1.AXIS.ns; } } else if (index === axis.y && !hideXAxis) { if (curr === line.length - 1) { lineChar = (axisSymbols === null || axisSymbols === void 0 ? void 0 : axisSymbols.e) || constants_1.AXIS.e; } else { lineChar = (axisSymbols === null || axisSymbols === void 0 ? void 0 : axisSymbols.we) || constants_1.AXIS.we; } } if (lineChar) { (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: curr, scaledY: index, symbol: lineChar }); } }); }); }; exports.drawAxis = drawAxis; /** * Initializes an empty graph based on plot dimensions and a given symbol. * @param {Object} params - Configuration options for the graph. * @param {number} params.plotWidth - Width of the plot area. * @param {number} params.plotHeight - Height of the plot area. * @param {string} params.emptySymbol - Symbol used to fill empty cells. * @returns {Graph} - An initialized empty graph matrix. */ var drawGraph = function (_a) { var plotWidth = _a.plotWidth, plotHeight = _a.plotHeight, emptySymbol = _a.emptySymbol; var callback = function () { return (0, coords_1.toEmpty)(plotWidth + 2, emptySymbol); }; return Array.from({ length: plotHeight + 2 }, callback); }; exports.drawGraph = drawGraph; /** * Renders the graph into a string format for output. * @param {Object} params - Configuration options for rendering the graph. * @param {Graph} params.graph - The graph matrix to render. * @returns {string} - The rendered graph as a string. */ var drawChart = function (_a) { var graph = _a.graph; return "\n".concat(graph.map(function (line) { return line.join(''); }).join('\n'), "\n"); }; exports.drawChart = drawChart; /** * Renders a custom line on the graph based on formatter specifications. * @param {Object} params - Configuration options for rendering custom lines. * @param {Point[]} params.sortedCoords - Sorted list of coordinates. * @param {number} params.scaledX - X-axis scaling. * @param {number} params.scaledY - Y-axis scaling. * @param {number} params.minY - Minimum Y value. * @param {number} params.minX - Minimum X value. * @param {MultiLine} params.input - Input data points. * @param {number[]} params.expansionX - X-axis expansion range. * @param {number[]} params.expansionY - Y-axis expansion range. * @param {function} params.toPlotCoordinates - Function to convert coordinates to plot positions. * @param {number} params.index - Current index in the coordinate array. * @param {function} params.lineFormatter - Custom function for line formatting. * @param {Graph} params.graph - The graph matrix to modify. */ var drawCustomLine = function (_a) { var sortedCoords = _a.sortedCoords, scaledX = _a.scaledX, scaledY = _a.scaledY, input = _a.input, index = _a.index, lineFormatter = _a.lineFormatter, graph = _a.graph, toPlotCoordinates = _a.toPlotCoordinates, expansionX = _a.expansionX, expansionY = _a.expansionY, minY = _a.minY, minX = _a.minX, debugMode = _a.debugMode; var lineFormatterArgs = { x: sortedCoords[index][0], y: sortedCoords[index][1], plotX: scaledX + 1, plotY: scaledY + 1, index: index, input: input[0], minY: minY, minX: minX, toPlotCoordinates: toPlotCoordinates, expansionX: expansionX, expansionY: expansionY, }; var customSymbols = lineFormatter(lineFormatterArgs); if (Array.isArray(customSymbols)) { customSymbols.forEach(function (_a) { var symbolX = _a.x, symbolY = _a.y, symbol = _a.symbol; (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: symbolX, scaledY: symbolY, symbol: symbol }); }); } else { (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: customSymbols.x, scaledY: customSymbols.y, symbol: customSymbols.symbol, }); } }; exports.drawCustomLine = drawCustomLine; /** * Renders a line between two points on the graph using defined chart symbols. * @param {Object} params - Configuration options for drawing a line. * @param {number} params.index - Current index in the coordinate array. * @param {Point[]} params.arr - List of points for the line. * @param {Graph} params.graph - The graph matrix to modify. * @param {number} params.scaledX - X-axis scaling. * @param {number} params.scaledY - Y-axis scaling. * @param {boolean} params.horizontalBarChart - Whether to fill the width of the bars. * @param {boolean} params.barChart - Whether to fill the width of the bars. * @param {number} params.plotHeight - Height of the plot area. * @param {string} params.emptySymbol - Symbol used to fill empty cells. * @param {Object} params.axis - Axis position. * @param {MaybePoint} axisCenter - Axis position selected by user. * @param {Symbols['chart']} params.chartSymbols - Symbols used for chart rendering. */ var drawLine = function (_a) { var index = _a.index, arr = _a.arr, graph = _a.graph, scaledX = _a.scaledX, scaledY = _a.scaledY, plotHeight = _a.plotHeight, emptySymbol = _a.emptySymbol, chartSymbols = _a.chartSymbols, horizontalBarChart = _a.horizontalBarChart, barChart = _a.barChart, axisCenter = _a.axisCenter, debugMode = _a.debugMode, axis = _a.axis; var _b = __read(arr[index], 2), currX = _b[0], currY = _b[1]; if (barChart || horizontalBarChart) { var positions = []; var axisCenterShift = axisCenter ? 0 : 1; // For vertical bar chart if (barChart) { var i = void 0; // Check if the value is positive or negative if (scaledY >= axis.y) { // For positive values, draw from the value down to the axis i = scaledY; while (i >= axis.y) { positions.push([i, scaledX + axisCenterShift]); i -= 1; } } else { // For negative values, draw from the value up to the axis i = scaledY; while (i <= axis.y) { positions.push([i, scaledX + axisCenterShift]); i += 1; } } } // For horizontal bar chart if (horizontalBarChart) { var i = void 0; if (scaledX >= axis.x) { // For positive values, draw rightward from the value to the axis i = scaledX; while (i >= axis.x) { positions.push([scaledY + 1, i]); i -= 1; } } else { // For negative values, draw leftward from the value to the axis i = scaledX; while (i <= axis.x) { positions.push([scaledY + 1, i]); i += 1; } } } // Draw all calculated positions positions.forEach(function (_a) { var _b = __read(_a, 2), y = _b[0], x = _b[1]; (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: x, scaledY: y, symbol: (chartSymbols === null || chartSymbols === void 0 ? void 0 : chartSymbols.area) || constants_1.CHART.area, }); }); return; } if (index - 1 >= 0) { var _c = __read(arr[index - 1], 2), prevX_1 = _c[0], prevY_1 = _c[1]; Array((0, coords_1.distance)(currY, prevY_1)) .fill('') .forEach(function (_, steps, array) { if (Math.round(prevY_1) > Math.round(currY)) { (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: scaledX, scaledY: scaledY + 1, symbol: (chartSymbols === null || chartSymbols === void 0 ? void 0 : chartSymbols.nse) || constants_1.CHART.nse, }); if (steps === array.length - 1) { (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: scaledX, scaledY: scaledY - steps, symbol: (chartSymbols === null || chartSymbols === void 0 ? void 0 : chartSymbols.wns) || constants_1.CHART.wns, }); } else { (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: scaledX, scaledY: scaledY - steps, symbol: (chartSymbols === null || chartSymbols === void 0 ? void 0 : chartSymbols.ns) || constants_1.CHART.ns, }); } } else { (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: scaledX, scaledY: scaledY + steps + 2, symbol: (chartSymbols === null || chartSymbols === void 0 ? void 0 : chartSymbols.wsn) || constants_1.CHART.wsn, }); (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: scaledX, scaledY: scaledY + steps + 1, symbol: (chartSymbols === null || chartSymbols === void 0 ? void 0 : chartSymbols.ns) || constants_1.CHART.ns, }); } }); if (Math.round(prevY_1) < Math.round(currY)) { (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: scaledX, scaledY: scaledY + 1, symbol: (chartSymbols === null || chartSymbols === void 0 ? void 0 : chartSymbols.sne) || constants_1.CHART.sne, }); } else if (Math.round(prevY_1) === Math.round(currY)) { if (graph[scaledY + 1][scaledX] === emptySymbol) { (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: scaledX, scaledY: scaledY + 1, symbol: (chartSymbols === null || chartSymbols === void 0 ? void 0 : chartSymbols.we) || constants_1.CHART.we, }); } } var distanceX = (0, coords_1.distance)(currX, prevX_1); Array(distanceX ? distanceX - 1 : 0) .fill('') .forEach(function (_, steps) { var thisY = plotHeight - Math.round(prevY_1); (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: Math.round(prevX_1) + steps + 1, scaledY: thisY, symbol: (chartSymbols === null || chartSymbols === void 0 ? void 0 : chartSymbols.we) || constants_1.CHART.we, }); }); } if (arr.length - 1 === index) { (0, exports.drawPosition)({ debugMode: debugMode, graph: graph, scaledX: scaledX + 1, scaledY: scaledY + 1, symbol: (chartSymbols === null || chartSymbols === void 0 ? void 0 : chartSymbols.we) || constants_1.CHART.we, }); } }; exports.drawLine = drawLine; /** * Applies shifts to the graph and adjusts empty symbols and scaling factors. * @param {Object} params - Configuration options for applying shifts. * @param {Graph} params.graph - The graph matrix. * @param {number} params.plotWidth - The width of the plot area. * @param {string} params.emptySymbol - The symbol used to fill empty cells. * @param {number[][]} params.scaledCoords - Scaled coordinates for shifting. * @param {number} params.xShift - X-axis shift offset. * @param {number} params.yShift - Y-axis shift offset. * @returns {Object} - An object indicating if the graph needs to be moved. */ var drawShift = function (_a) { var graph = _a.graph, plotWidth = _a.plotWidth, emptySymbol = _a.emptySymbol, scaledCoords = _a.scaledCoords, xShift = _a.xShift, yShift = _a.yShift; graph.push((0, coords_1.toEmpty)(plotWidth + 2, emptySymbol)); // bottom shift var step = plotWidth; scaledCoords.forEach(function (_a, index) { var _b = __read(_a, 1), x = _b[0]; if (scaledCoords[index - 1]) { var current = x - scaledCoords[index - 1][0]; step = current <= step ? current : step; } }); // x coords overlap var hasToBeMoved = step < xShift; if (hasToBeMoved) graph.push((0, coords_1.toEmpty)(plotWidth + 1, emptySymbol)); graph.forEach(function (line) { for (var i = 0; i <= yShift; i += 1) { line.unshift(emptySymbol); // left shift } }); return { hasToBeMoved: hasToBeMoved }; }; exports.drawShift = drawShift;