Skip to content

Commit 61f88b6

Browse files
committed
Plot multiple series together
1 parent f1934ea commit 61f88b6

File tree

3 files changed

+84
-37
lines changed

3 files changed

+84
-37
lines changed

AsciiChart.Sharp.TestApp/Program.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Linq;
23
using System.Text;
34

45
namespace AsciiChart.Sharp.TestApp
@@ -35,6 +36,13 @@ static void Main()
3536

3637
Console.WriteLine();
3738
Console.WriteLine(AsciiChart.Plot(series2, new Options{Height = 10}));
39+
40+
Console.WriteLine();
41+
Console.WriteLine(AsciiChart.Plot(
42+
Enumerable.Range(0, 6)
43+
.Select(i => Enumerable.Range(-40, 81).Select(x => Math.Abs(x) > 40 - i ? double.NaN : Math.Sqrt((40 - i) * (40 - i) - x * x) / 2))
44+
.ToList(),
45+
new Options { AxisLabelFormat = "0" }));
3846
}
3947
}
4048
}

AsciiChart.Sharp.Tests/Tests.cs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ public void TestPlot(double[] series, Options opts, string expected)
1616
Assert.AreEqual(Normalize(expected), Normalize(actual));
1717
}
1818

19-
static string Normalize(string text) => _normalize.Replace(text.Trim('\r', '\n') + Environment.NewLine, Environment.NewLine);
20-
2119
static object[][] Cases =
2220
{
2321
new object[] { new double[] { 1 }, null, " 1.00 ┼ " },
@@ -214,5 +212,37 @@ public void TestPlot(double[] series, Options opts, string expected)
214212
0.10 ┼╯"
215213
}
216214
};
215+
216+
[Test]
217+
[TestCaseSource(nameof(MultiCases))]
218+
public void TestPlotMulti(double[][] series, Options opts, string expected)
219+
{
220+
var actual = AsciiChart.Plot(series, opts);
221+
Assert.AreEqual(Normalize(expected), Normalize(actual));
222+
}
223+
224+
static object[][] MultiCases =
225+
{
226+
new object[]
227+
{
228+
new[] { new double[] { 0 }, new double[] { 1 }, new double[] { 2 } }, null, @"
229+
2.00 ┼
230+
1.00 ┼
231+
0.00 ┼"
232+
},
233+
new object[]
234+
{
235+
new[] { new[] { 0, 0, 2, 2, double.NaN }, new double[] { 1, 1, 1, 1, 1, 1, 1 }, new[] { double.NaN, double.NaN, double.NaN, 0, 0, 2, 2 } }, null, @"
236+
2.00 ┤ ╭─╴╭─
237+
1.00 ┼────│─
238+
0.00 ┼─╯╶─╯"
239+
},
240+
new object[]
241+
{
242+
new[] { new double[] { 0, 0, 0 }, new[] { double.NaN, 0, 0 }, new[] { double.NaN, double.NaN, 0 } }, null, " 0.00 ┼╶╶ "
243+
}
244+
};
245+
246+
static string Normalize(string text) => _normalize.Replace(text.Trim('\r', '\n') + Environment.NewLine, Environment.NewLine);
217247
}
218248
}

AsciiChart.Sharp/AsciiChart.cs

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,17 @@ public static class AsciiChart
1515
/// <param name="options">The plot options.</param>
1616
/// <returns>The ASCII Chart.</returns>
1717
public static string Plot(IEnumerable<double> series, Options options = null)
18+
{
19+
return Plot(new[] { series }, options);
20+
}
21+
22+
public static string Plot(IEnumerable<IEnumerable<double>> data, Options options = null)
1823
{
1924
options = options ?? new Options();
2025

21-
var seriesList = series.ToList();
22-
var min = seriesList.Where(v => !double.IsNaN(v)).Min();
23-
var max = seriesList.Max();
26+
var dataList = data.ToList();
27+
var min = dataList.SelectMany(s => s).Where(v => !double.IsNaN(v)).Min();
28+
var max = dataList.SelectMany(s => s).Max();
2429

2530
var range = Math.Abs(max - min);
2631
var ratio = range == 0 ? 0 : (options.Height ?? range) / range;
@@ -29,52 +34,56 @@ public static string Plot(IEnumerable<double> series, Options options = null)
2934
var rows = Math.Abs(max2 - min2);
3035

3136
var columnIndexOfFirstDataPoint = options.AxisLabelRightMargin + NumberOfNonDataColumns;
32-
var width = seriesList.Count + columnIndexOfFirstDataPoint;
37+
var width = dataList.Max(s => s.Count()) + columnIndexOfFirstDataPoint;
3338

3439
var resultArray = CreateAndFill2dArray(rows, width, options.Fill.ToString());
3540

3641
var yAxisLabels = GetYAxisLabels(max, range, rows, options);
3742
ApplyYAxisLabels(resultArray, yAxisLabels, columnIndexOfFirstDataPoint);
3843

39-
var rowIndex0 = Math.Round(seriesList[0] * ratio, MidpointRounding.AwayFromZero) - min2;
40-
if (!double.IsNaN(rowIndex0))
41-
{
42-
resultArray[(int)(rows - rowIndex0)][columnIndexOfFirstDataPoint - 1] = "┼";
43-
}
44-
45-
for (var x = 0; x < seriesList.Count - 1; x++)
44+
foreach (var series in dataList)
4645
{
47-
var rowIndex1 = Math.Round(seriesList[x + 1] * ratio, MidpointRounding.AwayFromZero) - min2;
48-
if (double.IsNaN(rowIndex0) && double.IsNaN(rowIndex1))
46+
var seriesList = series.ToList();
47+
var rowIndex0 = Math.Round(seriesList[0] * ratio, MidpointRounding.AwayFromZero) - min2;
48+
if (!double.IsNaN(rowIndex0))
4949
{
50-
continue;
50+
resultArray[(int)(rows - rowIndex0)][columnIndexOfFirstDataPoint - 1] = "┼";
5151
}
5252

53-
if (double.IsNaN(rowIndex0))
54-
{
55-
resultArray[(int)(rows - rowIndex1)][x + columnIndexOfFirstDataPoint] = "╶";
56-
}
57-
else if (double.IsNaN(rowIndex1))
58-
{
59-
resultArray[(int)(rows - rowIndex0)][x + columnIndexOfFirstDataPoint] = "╴";
60-
}
61-
else if (rowIndex0 == rowIndex1)
62-
{
63-
resultArray[(int)(rows - rowIndex0)][x + columnIndexOfFirstDataPoint] = "─";
64-
}
65-
else
53+
for (var x = 0; x < seriesList.Count - 1; x++)
6654
{
67-
resultArray[(int)(rows - rowIndex1)][x + columnIndexOfFirstDataPoint] = rowIndex0 > rowIndex1 ? "╰" : "╭";
68-
resultArray[(int)(rows - rowIndex0)][x + columnIndexOfFirstDataPoint] = rowIndex0 > rowIndex1 ? "╮" : "╯";
69-
var from = Math.Min(rowIndex0, rowIndex1);
70-
var to = Math.Max(rowIndex0, rowIndex1);
71-
for (var y = from + 1; y < to; y++)
55+
var rowIndex1 = Math.Round(seriesList[x + 1] * ratio, MidpointRounding.AwayFromZero) - min2;
56+
if (double.IsNaN(rowIndex0) && double.IsNaN(rowIndex1))
7257
{
73-
resultArray[(int)(rows - y)][x + columnIndexOfFirstDataPoint] = "│";
58+
continue;
7459
}
75-
}
7660

77-
rowIndex0 = rowIndex1;
61+
if (double.IsNaN(rowIndex0))
62+
{
63+
resultArray[(int)(rows - rowIndex1)][x + columnIndexOfFirstDataPoint] = "╶";
64+
}
65+
else if (double.IsNaN(rowIndex1))
66+
{
67+
resultArray[(int)(rows - rowIndex0)][x + columnIndexOfFirstDataPoint] = "╴";
68+
}
69+
else if (rowIndex0 == rowIndex1)
70+
{
71+
resultArray[(int)(rows - rowIndex0)][x + columnIndexOfFirstDataPoint] = "─";
72+
}
73+
else
74+
{
75+
resultArray[(int)(rows - rowIndex1)][x + columnIndexOfFirstDataPoint] = rowIndex0 > rowIndex1 ? "╰" : "╭";
76+
resultArray[(int)(rows - rowIndex0)][x + columnIndexOfFirstDataPoint] = rowIndex0 > rowIndex1 ? "╮" : "╯";
77+
var from = Math.Min(rowIndex0, rowIndex1);
78+
var to = Math.Max(rowIndex0, rowIndex1);
79+
for (var y = from + 1; y < to; y++)
80+
{
81+
resultArray[(int)(rows - y)][x + columnIndexOfFirstDataPoint] = "│";
82+
}
83+
}
84+
85+
rowIndex0 = rowIndex1;
86+
}
7887
}
7988

8089
return ToString(resultArray);

0 commit comments

Comments
 (0)