feat: add RenderSingleSeriesToPng for standalone underlying chart
This commit is contained in:
@@ -187,6 +187,130 @@ public static class SkiaChartRendererV2
|
||||
return imgData.ToArray();
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// Entry point — grafico standalone singolo sottostante (no certificato)
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
public static byte[] RenderSingleSeriesToPng(UnderlyingChartData data, int width = 1100, int height = 700, bool jpeg = false)
|
||||
{
|
||||
using var surface = SKSurface.Create(new SKImageInfo(width, height));
|
||||
var canvas = surface.Canvas;
|
||||
canvas.Clear(SKColors.White);
|
||||
|
||||
float titleBottom = DrawUnderlyingTitle(canvas, width, data.Nome);
|
||||
|
||||
float marginLeft = 70;
|
||||
float marginRight = 40;
|
||||
float marginTop = titleBottom + 10;
|
||||
float marginBottom = 45;
|
||||
|
||||
var plotArea = new SKRect(marginLeft, marginTop, width - marginRight, height - marginBottom);
|
||||
|
||||
var points = data.Points.OrderBy(p => p.Date).ToList();
|
||||
if (points.Count == 0)
|
||||
return Array.Empty<byte>();
|
||||
|
||||
DateTime minDate = points[0].Date;
|
||||
DateTime maxDate = points[^1].Date;
|
||||
double minY = (double)points.Min(p => p.Px);
|
||||
double maxY = (double)points.Max(p => p.Px);
|
||||
double range = maxY - minY;
|
||||
if (range == 0) range = Math.Max(minY * 0.1, 1);
|
||||
double margin = range * 0.1;
|
||||
minY -= margin;
|
||||
maxY += margin;
|
||||
|
||||
DrawGrid(canvas, plotArea, minDate, maxDate, minY, maxY);
|
||||
DrawUnderlyingAxisLabels(canvas, plotArea, minDate, maxDate, minY, maxY);
|
||||
DrawUnderlyingSeries(canvas, plotArea, points, minDate, maxDate, minY, maxY);
|
||||
|
||||
using var borderPaint = new SKPaint
|
||||
{
|
||||
Color = SKColors.Gray, StrokeWidth = 1,
|
||||
Style = SKPaintStyle.Stroke, IsAntialias = true,
|
||||
};
|
||||
canvas.DrawRect(plotArea, borderPaint);
|
||||
|
||||
using var image = surface.Snapshot();
|
||||
using var imgData = jpeg
|
||||
? image.Encode(SKEncodedImageFormat.Jpeg, 90)
|
||||
: image.Encode(SKEncodedImageFormat.Png, 95);
|
||||
return imgData.ToArray();
|
||||
}
|
||||
|
||||
private static float DrawUnderlyingTitle(SKCanvas canvas, int width, string nome)
|
||||
{
|
||||
float y = 15f;
|
||||
var boldFont = CreateFont(13f, bold: true);
|
||||
using var titlePaint = new SKPaint { Color = TitleColor, IsAntialias = true };
|
||||
canvas.DrawText(nome, width / 2f, y + 13, SKTextAlign.Center, boldFont, titlePaint);
|
||||
return y + 22 + 5;
|
||||
}
|
||||
|
||||
private static void DrawUnderlyingAxisLabels(SKCanvas canvas, SKRect area,
|
||||
DateTime minDate, DateTime maxDate, double minY, double maxY)
|
||||
{
|
||||
var font = CreateFont(11);
|
||||
using var paint = new SKPaint { Color = SKColors.DimGray, IsAntialias = true };
|
||||
|
||||
int ySteps = 8;
|
||||
for (int i = 0; i <= ySteps; i++)
|
||||
{
|
||||
double val = maxY - ((maxY - minY) / ySteps) * i;
|
||||
float y = area.Top + (area.Height / ySteps) * i;
|
||||
canvas.DrawText($"{val:F2}", area.Left - 55, y + 4, SKTextAlign.Left, font, paint);
|
||||
}
|
||||
|
||||
double totalDays = (maxDate - minDate).TotalDays;
|
||||
int intervalMonths = XAxisIntervalMonths(minDate, maxDate);
|
||||
string fmt = totalDays > 365 ? "MMM yy" : "dd/MM/yy";
|
||||
|
||||
if (intervalMonths > 0)
|
||||
{
|
||||
var d = XAxisStart(minDate, intervalMonths);
|
||||
while (d <= maxDate)
|
||||
{
|
||||
float x = DateToX(d, area, minDate, maxDate);
|
||||
if (x >= area.Left && x <= area.Right)
|
||||
canvas.DrawText(d.ToString(fmt), x, area.Bottom + 20, SKTextAlign.Center, font, paint);
|
||||
d = d.AddMonths(intervalMonths);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
canvas.DrawText(minDate.ToString("dd/MM/yy"), area.Left, area.Bottom + 20, SKTextAlign.Left, font, paint);
|
||||
canvas.DrawText(maxDate.ToString("dd/MM/yy"), area.Right, area.Bottom + 20, SKTextAlign.Right, font, paint);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DrawUnderlyingSeries(SKCanvas canvas, SKRect area,
|
||||
List<UnderlyingChartPoint> points,
|
||||
DateTime minDate, DateTime maxDate, double minY, double maxY)
|
||||
{
|
||||
using var paint = new SKPaint
|
||||
{
|
||||
Color = CertColor, StrokeWidth = 2f,
|
||||
Style = SKPaintStyle.Stroke, IsAntialias = true,
|
||||
StrokeCap = SKStrokeCap.Round, StrokeJoin = SKStrokeJoin.Round,
|
||||
};
|
||||
|
||||
using var path = new SKPath();
|
||||
bool first = true;
|
||||
foreach (var pt in points)
|
||||
{
|
||||
float x = DateToX(pt.Date, area, minDate, maxDate);
|
||||
float y = ValueToY((double)pt.Px, area, minY, maxY);
|
||||
y = Math.Max(area.Top, Math.Min(area.Bottom, y));
|
||||
if (first) { path.MoveTo(x, y); first = false; }
|
||||
else path.LineTo(x, y);
|
||||
}
|
||||
|
||||
canvas.Save();
|
||||
canvas.ClipRect(area);
|
||||
canvas.DrawPath(path, paint);
|
||||
canvas.Restore();
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// Titolo
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
Reference in New Issue
Block a user