simple-ascii-chart
Version:
Simple ascii chart generator
524 lines (523 loc) • 25 kB
JavaScript
;
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;