using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; namespace AxCopilot.Services.Agent; public class ChartSkill : IAgentTool { private sealed class Dataset { public string Name { get; init; } = ""; public List Values { get; init; } = new List(); public string Color { get; init; } = "#4B5EFC"; } private static readonly string[] Palette = new string[10] { "#4B5EFC", "#10B981", "#F59E0B", "#EF4444", "#8B5CF6", "#06B6D4", "#EC4899", "#84CC16", "#F97316", "#6366F1" }; private const string ChartCss = "\n/* Vertical Bar Chart */\n.vbar-chart { margin: 16px 0; }\n.vbar-bars { display: flex; align-items: flex-end; gap: 8px; height: 220px; padding: 0 8px; border-bottom: 2px solid #E5E7EB; }\n.vbar-group { flex: 1; display: flex; gap: 3px; align-items: flex-end; position: relative; }\n.vbar-bar { flex: 1; min-width: 18px; border-radius: 4px 4px 0 0; transition: opacity 0.2s; cursor: default; }\n.vbar-bar:hover { opacity: 0.8; }\n.vbar-label { text-align: center; font-size: 11px; color: #6B7280; margin-top: 6px; position: absolute; bottom: -24px; left: 0; right: 0; }\n\n/* Horizontal Bar Chart */\n.hbar-chart { margin: 12px 0; }\n.hbar-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }\n.hbar-label { min-width: 80px; text-align: right; font-size: 12px; color: #374151; font-weight: 500; }\n.hbar-track { flex: 1; height: 22px; background: #F3F4F6; border-radius: 6px; overflow: hidden; }\n.hbar-fill { height: 100%; border-radius: 6px; transition: width 0.6s ease; }\n.hbar-value { min-width: 50px; font-size: 12px; color: #6B7280; font-weight: 600; }\n\n/* Line/Area Chart */\n.line-chart-svg { width: 100%; max-width: 600px; height: auto; }\n\n/* Legend */\n.chart-legend { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 12px; padding-top: 8px; border-top: 1px solid #F3F4F6; }\n.legend-item { display: flex; align-items: center; gap: 6px; font-size: 12px; color: #374151; }\n.legend-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }\n\n/* Pie Legend */\n.pie-legend { display: flex; flex-direction: column; gap: 6px; }\n.pie-legend-item { display: flex; align-items: center; gap: 8px; font-size: 13px; color: #374151; }\n"; public string Name => "chart_create"; public string Description => "Create a styled HTML chart document with CSS/SVG-based charts. Supports chart types: bar, horizontal_bar, stacked_bar, line, area, pie, donut, radar, progress, comparison. Multiple charts can be placed in one document using the 'charts' array. Applies design mood from TemplateService (modern, professional, creative, etc.)."; public ToolParameterSchema Parameters { get { ToolParameterSchema obj = new ToolParameterSchema { Properties = new Dictionary { ["path"] = new ToolProperty { Type = "string", Description = "Output file path (.html). Relative to work folder." }, ["title"] = new ToolProperty { Type = "string", Description = "Document title" }, ["charts"] = new ToolProperty { Type = "array", Description = "Array of chart objects. Each chart: {\"type\": \"bar|horizontal_bar|stacked_bar|line|area|pie|donut|radar|progress|comparison\", \"title\": \"Chart Title\", \"labels\": [\"A\",\"B\",\"C\"], \"datasets\": [{\"name\": \"Series1\", \"values\": [10,20,30], \"color\": \"#4B5EFC\"}], \"unit\": \"%\"}", Items = new ToolProperty { Type = "object" } }, ["mood"] = new ToolProperty { Type = "string", Description = "Design mood: modern, professional, creative, dark, dashboard, etc. Default: dashboard" }, ["layout"] = new ToolProperty { Type = "string", Description = "Chart layout: 'single' (one per row) or 'grid' (2-column grid). Default: single" } } }; int num = 3; List list = new List(num); CollectionsMarshal.SetCount(list, num); Span span = CollectionsMarshal.AsSpan(list); span[0] = "path"; span[1] = "title"; span[2] = "charts"; obj.Required = list; return obj; } } public async Task ExecuteAsync(JsonElement args, AgentContext context, CancellationToken ct) { string path = args.GetProperty("path").GetString() ?? "chart.html"; string title = args.GetProperty("title").GetString() ?? "Chart"; JsonElement m; string mood = (args.TryGetProperty("mood", out m) ? (m.GetString() ?? "dashboard") : "dashboard"); JsonElement l; string layout = (args.TryGetProperty("layout", out l) ? (l.GetString() ?? "single") : "single"); string fullPath = FileReadTool.ResolvePath(path, context.WorkFolder); if (context.ActiveTab == "Cowork") { fullPath = AgentContext.EnsureTimestampedPath(fullPath); } if (!fullPath.EndsWith(".html", StringComparison.OrdinalIgnoreCase)) { fullPath += ".html"; } if (!context.IsPathAllowed(fullPath)) { return ToolResult.Fail("경로 접근 차단: " + fullPath); } if (!(await context.CheckWritePermissionAsync(Name, fullPath))) { return ToolResult.Fail("쓰기 권한 거부: " + fullPath); } string dir = Path.GetDirectoryName(fullPath); if (!string.IsNullOrEmpty(dir)) { Directory.CreateDirectory(dir); } if (!args.TryGetProperty("charts", out var chartsEl) || chartsEl.ValueKind != JsonValueKind.Array) { return ToolResult.Fail("charts 파라미터가 필요합니다 (배열 형식)."); } int chartCount = chartsEl.GetArrayLength(); StringBuilder body = new StringBuilder(); StringBuilder stringBuilder = body; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(9, 1, stringBuilder); handler.AppendLiteral("

"); handler.AppendFormatted(Escape(title)); handler.AppendLiteral("

"); stringBuilder.AppendLine(ref handler); if (layout == "grid" && chartCount > 1) { body.AppendLine("
"); } int chartIdx = 0; foreach (JsonElement chartEl in chartsEl.EnumerateArray()) { string chartHtml = RenderChart(chartEl, chartIdx); body.AppendLine("
"); body.AppendLine(chartHtml); body.AppendLine("
"); chartIdx++; } if (layout == "grid" && chartCount > 1) { body.AppendLine("
"); } string css = TemplateService.GetCss(mood) + "\n\n/* Vertical Bar Chart */\n.vbar-chart { margin: 16px 0; }\n.vbar-bars { display: flex; align-items: flex-end; gap: 8px; height: 220px; padding: 0 8px; border-bottom: 2px solid #E5E7EB; }\n.vbar-group { flex: 1; display: flex; gap: 3px; align-items: flex-end; position: relative; }\n.vbar-bar { flex: 1; min-width: 18px; border-radius: 4px 4px 0 0; transition: opacity 0.2s; cursor: default; }\n.vbar-bar:hover { opacity: 0.8; }\n.vbar-label { text-align: center; font-size: 11px; color: #6B7280; margin-top: 6px; position: absolute; bottom: -24px; left: 0; right: 0; }\n\n/* Horizontal Bar Chart */\n.hbar-chart { margin: 12px 0; }\n.hbar-row { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }\n.hbar-label { min-width: 80px; text-align: right; font-size: 12px; color: #374151; font-weight: 500; }\n.hbar-track { flex: 1; height: 22px; background: #F3F4F6; border-radius: 6px; overflow: hidden; }\n.hbar-fill { height: 100%; border-radius: 6px; transition: width 0.6s ease; }\n.hbar-value { min-width: 50px; font-size: 12px; color: #6B7280; font-weight: 600; }\n\n/* Line/Area Chart */\n.line-chart-svg { width: 100%; max-width: 600px; height: auto; }\n\n/* Legend */\n.chart-legend { display: flex; gap: 16px; flex-wrap: wrap; margin-top: 12px; padding-top: 8px; border-top: 1px solid #F3F4F6; }\n.legend-item { display: flex; align-items: center; gap: 6px; font-size: 12px; color: #374151; }\n.legend-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }\n\n/* Pie Legend */\n.pie-legend { display: flex; flex-direction: column; gap: 6px; }\n.pie-legend-item { display: flex; align-items: center; gap: 8px; font-size: 13px; color: #374151; }\n"; string html = $"\n\n\n\n\n{Escape(title)}\n\n\n\n
\n{body}\n
\n\n"; await File.WriteAllTextAsync(fullPath, html, Encoding.UTF8, ct); return ToolResult.Ok($"차트 문서 생성 완료: {fullPath} ({chartCount}개 차트)", fullPath); } private string RenderChart(JsonElement chart, int idx) { JsonElement value; string text = (chart.TryGetProperty("type", out value) ? (value.GetString() ?? "bar") : "bar"); JsonElement value2; string text2 = (chart.TryGetProperty("title", out value2) ? (value2.GetString() ?? "") : ""); JsonElement value3; string unit = (chart.TryGetProperty("unit", out value3) ? (value3.GetString() ?? "") : ""); List labels = ParseStringArray(chart, "labels"); List list = ParseDatasets(chart); StringBuilder stringBuilder = new StringBuilder(); if (!string.IsNullOrEmpty(text2)) { StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(37, 1, stringBuilder2); handler.AppendLiteral("

"); handler.AppendFormatted(Escape(text2)); handler.AppendLiteral("

"); stringBuilder3.AppendLine(ref handler); } switch (text) { case "bar": stringBuilder.Append(RenderBarChart(labels, list, unit, horizontal: false)); break; case "horizontal_bar": stringBuilder.Append(RenderBarChart(labels, list, unit, horizontal: true)); break; case "stacked_bar": stringBuilder.Append(RenderStackedBar(labels, list, unit)); break; case "line": case "area": stringBuilder.Append(RenderLineChart(labels, list, unit, text == "area")); break; case "pie": case "donut": stringBuilder.Append(RenderPieChart(labels, list, text == "donut")); break; case "progress": stringBuilder.Append(RenderProgressChart(labels, list, unit)); break; case "comparison": stringBuilder.Append(RenderComparisonChart(labels, list, unit)); break; case "radar": stringBuilder.Append(RenderRadarChart(labels, list)); break; default: stringBuilder.Append(RenderBarChart(labels, list, unit, horizontal: false)); break; } if (list.Count > 1) { stringBuilder.AppendLine("
"); foreach (Dataset item in list) { StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(85, 2, stringBuilder2); handler.AppendLiteral(""); handler.AppendFormatted(Escape(item.Name)); handler.AppendLiteral(""); stringBuilder4.AppendLine(ref handler); } stringBuilder.AppendLine("
"); } return stringBuilder.ToString(); } private static string RenderBarChart(List labels, List datasets, string unit, bool horizontal) { double num = datasets.SelectMany((Dataset d) => d.Values).DefaultIfEmpty(1.0).Max(); if (num <= 0.0) { num = 1.0; } StringBuilder stringBuilder = new StringBuilder(); if (horizontal) { stringBuilder.AppendLine("
"); for (int num2 = 0; num2 < labels.Count; num2++) { double num3 = ((datasets.Count > 0 && num2 < datasets[0].Values.Count) ? datasets[0].Values[num2] : 0.0); int value = (int)(num3 / num * 100.0); string value2 = ((datasets.Count > 0) ? datasets[0].Color : Palette[0]); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(54, 1, stringBuilder2); handler.AppendLiteral("
"); handler.AppendFormatted(Escape(labels[num2])); handler.AppendLiteral(""); stringBuilder3.AppendLine(ref handler); stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(88, 2, stringBuilder2); handler.AppendLiteral("
"); stringBuilder4.AppendLine(ref handler); stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(38, 2, stringBuilder2); handler.AppendLiteral(""); handler.AppendFormatted(num3, "G"); handler.AppendFormatted(unit); handler.AppendLiteral("
"); stringBuilder5.AppendLine(ref handler); } stringBuilder.AppendLine("
"); } else { stringBuilder.AppendLine("
"); stringBuilder.AppendLine("
"); for (int num4 = 0; num4 < labels.Count; num4++) { stringBuilder.AppendLine("
"); StringBuilder stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler; foreach (Dataset dataset in datasets) { double num5 = ((num4 < dataset.Values.Count) ? dataset.Values[num4] : 0.0); int value3 = (int)(num5 / num * 100.0); stringBuilder2 = stringBuilder; StringBuilder stringBuilder6 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(67, 4, stringBuilder2); handler.AppendLiteral("
"); stringBuilder6.AppendLine(ref handler); } stringBuilder2 = stringBuilder; StringBuilder stringBuilder7 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(30, 1, stringBuilder2); handler.AppendLiteral("
"); handler.AppendFormatted(Escape(labels[num4])); handler.AppendLiteral("
"); stringBuilder7.AppendLine(ref handler); stringBuilder.AppendLine("
"); } stringBuilder.AppendLine("
"); } return stringBuilder.ToString(); } private static string RenderStackedBar(List labels, List datasets, string unit) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("
"); int i; for (i = 0; i < labels.Count; i++) { double num = datasets.Sum((Dataset ds) => (i < ds.Values.Count) ? ds.Values[i] : 0.0); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(54, 1, stringBuilder2); handler.AppendLiteral("
"); handler.AppendFormatted(Escape(labels[i])); handler.AppendLiteral(""); stringBuilder3.AppendLine(ref handler); stringBuilder.AppendLine("
"); foreach (Dataset dataset in datasets) { double num2 = ((i < dataset.Values.Count) ? dataset.Values[i] : 0.0); int value = ((num > 0.0) ? ((int)(num2 / num * 100.0)) : 0); stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(63, 5, stringBuilder2); handler.AppendLiteral("
"); stringBuilder4.AppendLine(ref handler); } stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(44, 2, stringBuilder2); handler.AppendLiteral("
"); handler.AppendFormatted(num, "G"); handler.AppendFormatted(unit); handler.AppendLiteral("
"); stringBuilder5.AppendLine(ref handler); } stringBuilder.AppendLine("
"); return stringBuilder.ToString(); } private static string RenderLineChart(List labels, List datasets, string unit, bool isArea) { List source = datasets.SelectMany((Dataset d) => d.Values).ToList(); double num = source.DefaultIfEmpty(1.0).Max(); double num2 = source.DefaultIfEmpty(0.0).Min(); if (num <= num2) { num = num2 + 1.0; } int num3 = 600; int num4 = 300; int num5 = 50; int num6 = 20; int num7 = 20; int num8 = 40; int num9 = num3 - num5 - num6; int num10 = num4 - num7 - num8; int count = labels.Count; StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(80, 2, stringBuilder2); handler.AppendLiteral(""); stringBuilder3.AppendLine(ref handler); for (int num11 = 0; num11 <= 4; num11++) { double num12 = (double)(num7 + num10) - (double)(num10 * num11) / 4.0; double value = num2 + (num - num2) * (double)num11 / 4.0; stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(65, 4, stringBuilder2); handler.AppendLiteral(""); stringBuilder4.AppendLine(ref handler); stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(71, 4, stringBuilder2); handler.AppendLiteral(""); handler.AppendFormatted(value, "G3"); handler.AppendFormatted(unit); handler.AppendLiteral(""); stringBuilder5.AppendLine(ref handler); } for (int num13 = 0; num13 < count; num13++) { double value2 = (double)num5 + ((count > 1) ? ((double)(num9 * num13) / (double)(count - 1)) : ((double)num9 / 2.0)); stringBuilder2 = stringBuilder; StringBuilder stringBuilder6 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(74, 3, stringBuilder2); handler.AppendLiteral(""); handler.AppendFormatted(Escape(labels[num13])); handler.AppendLiteral(""); stringBuilder6.AppendLine(ref handler); } foreach (Dataset dataset in datasets) { List<(double, double)> list = new List<(double, double)>(); for (int num14 = 0; num14 < Math.Min(count, dataset.Values.Count); num14++) { double item = (double)num5 + ((count > 1) ? ((double)(num9 * num14) / (double)(count - 1)) : ((double)num9 / 2.0)); double item2 = (double)(num7 + num10) - (dataset.Values[num14] - num2) / (num - num2) * (double)num10; list.Add((item, item2)); } string text = string.Join(" ", list.Select<(double, double), string>(((double x, double y) p, int i) => $"{((i == 0) ? "M" : "L")}{p.x:F1},{p.y:F1}")); if (isArea && list.Count > 1) { string value3 = text + $" L{list.Last().Item1:F1},{num7 + num10} L{list.First().Item1:F1},{num7 + num10} Z"; stringBuilder2 = stringBuilder; StringBuilder stringBuilder7 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(35, 2, stringBuilder2); handler.AppendLiteral(""); stringBuilder7.AppendLine(ref handler); } stringBuilder2 = stringBuilder; StringBuilder stringBuilder8 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(100, 2, stringBuilder2); handler.AppendLiteral(""); stringBuilder8.AppendLine(ref handler); foreach (var item5 in list) { double item3 = item5.Item1; double item4 = item5.Item2; stringBuilder2 = stringBuilder; StringBuilder stringBuilder9 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(67, 3, stringBuilder2); handler.AppendLiteral(""); stringBuilder9.AppendLine(ref handler); } } stringBuilder.AppendLine(""); return stringBuilder.ToString(); } private static string RenderPieChart(List labels, List datasets, bool isDonut) { List list = ((datasets.Count > 0) ? datasets[0].Values : new List()); double num = list.Sum(); if (num <= 0.0) { num = 1.0; } int num2 = 150; int num3 = 150; int num4 = 120; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("
"); stringBuilder.AppendLine(""); double num5 = -90.0; for (int i = 0; i < Math.Min(list.Count, labels.Count); i++) { double num6 = list[i] / num; double num7 = num6 * 360.0; double num8 = num5 + num7; double value = (double)num2 + (double)num4 * Math.Cos(num5 * Math.PI / 180.0); double value2 = (double)num3 + (double)num4 * Math.Sin(num5 * Math.PI / 180.0); double value3 = (double)num2 + (double)num4 * Math.Cos(num8 * Math.PI / 180.0); double value4 = (double)num3 + (double)num4 * Math.Sin(num8 * Math.PI / 180.0); int value5 = ((num7 > 180.0) ? 1 : 0); string value6 = ((i < Palette.Length) ? Palette[i] : Palette[i % Palette.Length]); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(37, 10, stringBuilder2); handler.AppendLiteral(""); stringBuilder3.AppendLine(ref handler); num5 = num8; } if (isDonut) { StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(39, 3, stringBuilder2); handler.AppendLiteral(""); stringBuilder4.AppendLine(ref handler); } stringBuilder.AppendLine(""); stringBuilder.AppendLine("
"); for (int j = 0; j < Math.Min(list.Count, labels.Count); j++) { string value7 = ((j < Palette.Length) ? Palette[j] : Palette[j % Palette.Length]); double value8 = list[j] / num * 100.0; StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(142, 3, stringBuilder2); handler.AppendLiteral("
"); handler.AppendFormatted(Escape(labels[j])); handler.AppendLiteral(" ("); handler.AppendFormatted(value8, "F1"); handler.AppendLiteral("%)
"); stringBuilder5.AppendLine(ref handler); } stringBuilder.AppendLine("
"); return stringBuilder.ToString(); } private static string RenderProgressChart(List labels, List datasets, string unit) { List list = ((datasets.Count > 0) ? datasets[0].Values : new List()); StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < Math.Min(labels.Count, list.Count); i++) { double value = Math.Clamp(list[i], 0.0, 100.0); string value2 = ((i < Palette.Length) ? Palette[i] : Palette[i % Palette.Length]); if (datasets.Count > 0 && !string.IsNullOrEmpty(datasets[0].Color)) { value2 = datasets[0].Color; } StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(218, 3, stringBuilder2); handler.AppendLiteral("
"); handler.AppendFormatted(Escape(labels[i])); handler.AppendLiteral(""); handler.AppendFormatted(list[i], "G"); handler.AppendFormatted(unit); handler.AppendLiteral("
"); stringBuilder3.AppendLine(ref handler); stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(96, 2, stringBuilder2); handler.AppendLiteral("
"); stringBuilder4.AppendLine(ref handler); } return stringBuilder.ToString(); } private static string RenderComparisonChart(List labels, List datasets, string unit) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(""); stringBuilder.AppendLine(""); foreach (Dataset dataset in datasets) { StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(60, 2, stringBuilder2); handler.AppendLiteral(""); stringBuilder3.AppendLine(ref handler); } stringBuilder.AppendLine(""); for (int i = 0; i < labels.Count; i++) { StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(93, 1, stringBuilder2); handler.AppendLiteral(""); stringBuilder4.Append(ref handler); foreach (Dataset dataset2 in datasets) { double value = ((i < dataset2.Values.Count) ? dataset2.Values[i] : 0.0); stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(53, 2, stringBuilder2); handler.AppendLiteral(""); stringBuilder5.Append(ref handler); } stringBuilder.AppendLine(""); } stringBuilder.AppendLine("
항목"); handler.AppendFormatted(Escape(dataset.Name)); handler.AppendLiteral("
"); handler.AppendFormatted(Escape(labels[i])); handler.AppendLiteral(""); handler.AppendFormatted(value, "G"); handler.AppendFormatted(unit); handler.AppendLiteral("
"); return stringBuilder.ToString(); } private static string RenderRadarChart(List labels, List datasets) { int cx = 150; int cy = 150; int r = 110; int n = labels.Count; if (n < 3) { return "

레이더 차트는 최소 3개 항목이 필요합니다.

"; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine(""); for (int i = 1; i <= 4; i++) { double lr = (double)(r * i) / 4.0; string value = string.Join(" ", Enumerable.Range(0, n).Select(delegate(int num5) { double num4 = (360.0 / (double)n * (double)num5 - 90.0) * Math.PI / 180.0; return $"{(double)cx + lr * Math.Cos(num4):F1},{(double)cy + lr * Math.Sin(num4):F1}"; })); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder3 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(66, 1, stringBuilder2); handler.AppendLiteral(""); stringBuilder3.AppendLine(ref handler); } for (int num = 0; num < n; num++) { double num2 = (360.0 / (double)n * (double)num - 90.0) * Math.PI / 180.0; double value2 = (double)cx + (double)r * Math.Cos(num2); double value3 = (double)cy + (double)r * Math.Sin(num2); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder4 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(65, 4, stringBuilder2); handler.AppendLiteral(""); stringBuilder4.AppendLine(ref handler); double value4 = (double)cx + (double)(r + 16) * Math.Cos(num2); double num3 = (double)cy + (double)(r + 16) * Math.Sin(num2); stringBuilder2 = stringBuilder; StringBuilder stringBuilder5 = stringBuilder2; handler = new StringBuilder.AppendInterpolatedStringHandler(74, 3, stringBuilder2); handler.AppendLiteral(""); handler.AppendFormatted(Escape(labels[num])); handler.AppendLiteral(""); stringBuilder5.AppendLine(ref handler); } double maxVal = datasets.SelectMany((Dataset d) => d.Values).DefaultIfEmpty(1.0).Max(); if (maxVal <= 0.0) { maxVal = 1.0; } foreach (Dataset ds in datasets) { string value5 = string.Join(" ", Enumerable.Range(0, n).Select(delegate(int num5) { double num4 = ((num5 < ds.Values.Count) ? ds.Values[num5] : 0.0); double num6 = (double)r * num4 / maxVal; double num7 = (360.0 / (double)n * (double)num5 - 90.0) * Math.PI / 180.0; return $"{(double)cx + num6 * Math.Cos(num7):F1},{(double)cy + num6 * Math.Sin(num7):F1}"; })); StringBuilder stringBuilder2 = stringBuilder; StringBuilder stringBuilder6 = stringBuilder2; StringBuilder.AppendInterpolatedStringHandler handler = new StringBuilder.AppendInterpolatedStringHandler(74, 3, stringBuilder2); handler.AppendLiteral(""); stringBuilder6.AppendLine(ref handler); } stringBuilder.AppendLine(""); return stringBuilder.ToString(); } private static List ParseStringArray(JsonElement parent, string prop) { if (!parent.TryGetProperty(prop, out var value) || value.ValueKind != JsonValueKind.Array) { return new List(); } return (from e in value.EnumerateArray() select e.GetString() ?? "").ToList(); } private List ParseDatasets(JsonElement chart) { if (!chart.TryGetProperty("datasets", out var value) || value.ValueKind != JsonValueKind.Array) { double value6; if (chart.TryGetProperty("values", out var value2) && value2.ValueKind == JsonValueKind.Array) { return new List { new Dataset { Name = "Data", Values = (from v in value2.EnumerateArray() select v.TryGetDouble(out value6) ? value6 : 0.0).ToList(), Color = Palette[0] } }; } return new List(); } List list = new List(); int num = 0; foreach (JsonElement item in value.EnumerateArray()) { JsonElement value3; string name = (item.TryGetProperty("name", out value3) ? (value3.GetString() ?? $"Series{num + 1}") : $"Series{num + 1}"); JsonElement value4; string color = (item.TryGetProperty("color", out value4) ? (value4.GetString() ?? Palette[num % Palette.Length]) : Palette[num % Palette.Length]); List values = new List(); if (item.TryGetProperty("values", out var value5) && value5.ValueKind == JsonValueKind.Array) { values = (from e in value5.EnumerateArray() select e.TryGetDouble(out var value6) ? value6 : 0.0).ToList(); } list.Add(new Dataset { Name = name, Values = values, Color = color }); num++; } return list; } private static string Escape(string s) { return s.Replace("&", "&").Replace("<", "<").Replace(">", ">") .Replace("\"", """); } private static string FormatSize(long bytes) { if (1 == 0) { } string result = ((bytes < 1024) ? $"{bytes}B" : ((bytes >= 1048576) ? $"{(double)bytes / 1048576.0:F1}MB" : $"{(double)bytes / 1024.0:F1}KB")); if (1 == 0) { } return result; } }