feat: Circle/RoundedRectangle-Zeichenbefehle im Canvas-Backend, Fehlzeitenkalender-Kacheln größer
CI / build-and-test (push) Waiting to run
CI / build-and-test (push) Waiting to run
Neue DrawingCommand-Primitives DrawCircle und DrawRoundedRectangle (SVG-Rendering plus FLOWDRAWBOX-Paginierung). Anwesenheitskalender nutzt RoundedRectangle für die Tageskacheln und verkleinert Monatsabstand/Kachel-Innenabstand, damit die Kacheln den Rasterplatz sichtbar besser ausfüllen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -44,11 +44,18 @@ public static class StudentAttendanceCalendarDrawingBuilder
|
|||||||
// würde am rechten Rand abgeschnitten (SVG overflow="hidden").
|
// würde am rechten Rand abgeschnitten (SVG overflow="hidden").
|
||||||
var contentWidth = width;
|
var contentWidth = width;
|
||||||
var cellHeight = 10 * scale;
|
var cellHeight = 10 * scale;
|
||||||
var monthGapX = 10f;
|
// War 10 - bei 3 Monaten nebeneinander größer als eine einzelne Tageskachel und zog damit
|
||||||
|
// spürbar Platz von den Kacheln ab, ohne selbst als Inhalt wahrgenommen zu werden.
|
||||||
|
var monthGapX = 6f;
|
||||||
var first = options.NormalizedStartMonth;
|
var first = options.NormalizedStartMonth;
|
||||||
var monthCount = options.NormalizedMonthCount;
|
var monthCount = options.NormalizedMonthCount;
|
||||||
var monthWidth = (contentWidth - monthGapX * (monthCount - 1)) / monthCount;
|
var monthWidth = (contentWidth - monthGapX * (monthCount - 1)) / monthCount;
|
||||||
var cellWidth = monthWidth / 7;
|
var cellWidth = monthWidth / 7;
|
||||||
|
// Kachel-Zwischenraum (Trennung zu Nachbarzellen) deutlich knapper als vorher (war "scale",
|
||||||
|
// also bis zu 1mm auf jeder Seite - bei einer ~7mm breiten Zelle ein gutes Viertel reiner
|
||||||
|
// Leerraum). Die Kachel selbst füllt dadurch ihren Rasterplatz sichtbar besser aus.
|
||||||
|
var cellInset = scale * .4f;
|
||||||
|
var cellCornerRadius = Math.Min(cellWidth, cellHeight) * .2f;
|
||||||
var commands = new List<DrawingCommand>();
|
var commands = new List<DrawingCommand>();
|
||||||
var y = 0f;
|
var y = 0f;
|
||||||
commands.Add(new DrawStringEx(0, y, 14 * scale, contentWidth, "Anwesenheit",
|
commands.Add(new DrawStringEx(0, y, 14 * scale, contentWidth, "Anwesenheit",
|
||||||
@@ -86,8 +93,9 @@ public static class StudentAttendanceCalendarDrawingBuilder
|
|||||||
var row = index / 7;
|
var row = index / 7;
|
||||||
var x = xOffset + column * cellWidth;
|
var x = xOffset + column * cellWidth;
|
||||||
var cellY = monthY + row * cellHeight;
|
var cellY = monthY + row * cellHeight;
|
||||||
commands.Add(new DrawRectangle(x + scale, cellY, cellWidth - 2 * scale, cellHeight - scale,
|
commands.Add(new DrawRoundedRectangle(x + cellInset, cellY, cellWidth - 2 * cellInset,
|
||||||
"#D1D5DB", .35f, day.HasSignal ? day.SignalColorHex : "#FFFFFF"));
|
cellHeight - cellInset, cellCornerRadius, "#D1D5DB", .35f,
|
||||||
|
day.HasSignal ? day.SignalColorHex : "#FFFFFF"));
|
||||||
commands.Add(new DrawStringEx(x, cellY + scale, cellHeight - 2 * scale, cellWidth,
|
commands.Add(new DrawStringEx(x, cellY + scale, cellHeight - 2 * scale, cellWidth,
|
||||||
day.HasSignal ? day.SignalCode : day.DayNumber, DrawingTextAlignment.AlignCenter, 7 * scale,
|
day.HasSignal ? day.SignalCode : day.DayNumber, DrawingTextAlignment.AlignCenter, 7 * scale,
|
||||||
Color: day.HasSignal ? "#FFFFFF" : "#374151", Bold: day.HasSignal));
|
Color: day.HasSignal ? "#FFFFFF" : "#374151", Bold: day.HasSignal));
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ public sealed class TemplatingTests : IDisposable
|
|||||||
new Dictionary<string, byte[]>());
|
new Dictionary<string, byte[]>());
|
||||||
var drawing = new DrawingValue(
|
var drawing = new DrawingValue(
|
||||||
[new DrawRectangle(0, 0, 100, 60, "#1D4ED8", 1, "#EFF6FF"),
|
[new DrawRectangle(0, 0, 100, 60, "#1D4ED8", 1, "#EFF6FF"),
|
||||||
|
new DrawRoundedRectangle(2, 2, 30, 15, 3, "#1D4ED8", 1, "#EFF6FF"),
|
||||||
|
new DrawCircle(90, 10, 6, "#1D4ED8", 1, "#EFF6FF"),
|
||||||
new DrawString(5, 5, "Externer Inhalt", 11, Bold: true),
|
new DrawString(5, 5, "Externer Inhalt", 11, Bold: true),
|
||||||
new MoveTo(5, 25), new LineTo(95, 25, "#DC2626", 1.5f),
|
new MoveTo(5, 25), new LineTo(95, 25, "#DC2626", 1.5f),
|
||||||
new DrawLine(5, 35, 95, 50, "#059669", 1)], 60);
|
new DrawLine(5, 35, 95, 50, "#059669", 1)], 60);
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ public sealed record DrawLine(float X1, float Y1, float X2, float Y2,
|
|||||||
string Color = "#000000", float StrokeWidth = 1) : DrawingCommand;
|
string Color = "#000000", float StrokeWidth = 1) : DrawingCommand;
|
||||||
public sealed record DrawRectangle(float X, float Y, float Width, float Height,
|
public sealed record DrawRectangle(float X, float Y, float Width, float Height,
|
||||||
string StrokeColor = "#000000", float StrokeWidth = 1, string FillColor = "none") : DrawingCommand;
|
string StrokeColor = "#000000", float StrokeWidth = 1, string FillColor = "none") : DrawingCommand;
|
||||||
|
public sealed record DrawRoundedRectangle(float X, float Y, float Width, float Height, float CornerRadius,
|
||||||
|
string StrokeColor = "#000000", float StrokeWidth = 1, string FillColor = "none") : DrawingCommand;
|
||||||
|
public sealed record DrawCircle(float CenterX, float CenterY, float Radius,
|
||||||
|
string StrokeColor = "#000000", float StrokeWidth = 1, string FillColor = "none") : DrawingCommand;
|
||||||
public sealed record DrawImage(float X, float Y, float Width, float Height, byte[] Data, string MimeType) : DrawingCommand;
|
public sealed record DrawImage(float X, float Y, float Width, float Height, byte[] Data, string MimeType) : DrawingCommand;
|
||||||
/// <summary>Declarative drawing supplied by an external data provider. Coordinates use the template layout unit; font sizes use points.</summary>
|
/// <summary>Declarative drawing supplied by an external data provider. Coordinates use the template layout unit; font sizes use points.</summary>
|
||||||
public sealed record DrawingValue(IReadOnlyList<DrawingCommand> Commands, float ContentHeight)
|
public sealed record DrawingValue(IReadOnlyList<DrawingCommand> Commands, float ContentHeight)
|
||||||
@@ -56,6 +60,13 @@ public sealed class DrawingCanvas
|
|||||||
public void DrawRectangle(float x, float y, float width, float height, string strokeColor = "#000000",
|
public void DrawRectangle(float x, float y, float width, float height, string strokeColor = "#000000",
|
||||||
float strokeWidth = 1, string fillColor = "none") =>
|
float strokeWidth = 1, string fillColor = "none") =>
|
||||||
_commands.Add(new LehrerApp.Templating.DrawRectangle(x, y, width, height, strokeColor, strokeWidth, fillColor));
|
_commands.Add(new LehrerApp.Templating.DrawRectangle(x, y, width, height, strokeColor, strokeWidth, fillColor));
|
||||||
|
public void DrawRoundedRectangle(float x, float y, float width, float height, float cornerRadius,
|
||||||
|
string strokeColor = "#000000", float strokeWidth = 1, string fillColor = "none") =>
|
||||||
|
_commands.Add(new LehrerApp.Templating.DrawRoundedRectangle(x, y, width, height, cornerRadius,
|
||||||
|
strokeColor, strokeWidth, fillColor));
|
||||||
|
public void DrawCircle(float centerX, float centerY, float radius, string strokeColor = "#000000",
|
||||||
|
float strokeWidth = 1, string fillColor = "none") =>
|
||||||
|
_commands.Add(new LehrerApp.Templating.DrawCircle(centerX, centerY, radius, strokeColor, strokeWidth, fillColor));
|
||||||
public void DrawImage(float x, float y, float width, float height, byte[] data, string mimeType) =>
|
public void DrawImage(float x, float y, float width, float height, byte[] data, string mimeType) =>
|
||||||
_commands.Add(new LehrerApp.Templating.DrawImage(x, y, width, height, data, mimeType));
|
_commands.Add(new LehrerApp.Templating.DrawImage(x, y, width, height, data, mimeType));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -577,15 +577,18 @@ internal static class DrawingElementRenderer
|
|||||||
private static float? CommandY(DrawingCommand command) => command switch
|
private static float? CommandY(DrawingCommand command) => command switch
|
||||||
{
|
{
|
||||||
DrawString s => s.Y, DrawStringEx s => s.Y, MoveTo m => m.Y, LineTo l => l.Y,
|
DrawString s => s.Y, DrawStringEx s => s.Y, MoveTo m => m.Y, LineTo l => l.Y,
|
||||||
DrawRectangle r => r.Y, DrawImage i => i.Y, DrawLine ln => Math.Min(ln.Y1, ln.Y2), _ => null,
|
DrawRectangle r => r.Y, DrawRoundedRectangle r => r.Y, DrawImage i => i.Y,
|
||||||
|
DrawLine ln => Math.Min(ln.Y1, ln.Y2), DrawCircle c => c.CenterY - c.Radius, _ => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
private static DrawingCommand ShiftY(DrawingCommand command, float dy) => command switch
|
private static DrawingCommand ShiftY(DrawingCommand command, float dy) => command switch
|
||||||
{
|
{
|
||||||
DrawString s => s with { Y = s.Y - dy }, DrawStringEx s => s with { Y = s.Y - dy },
|
DrawString s => s with { Y = s.Y - dy }, DrawStringEx s => s with { Y = s.Y - dy },
|
||||||
MoveTo m => m with { Y = m.Y - dy }, LineTo l => l with { Y = l.Y - dy },
|
MoveTo m => m with { Y = m.Y - dy }, LineTo l => l with { Y = l.Y - dy },
|
||||||
DrawRectangle r => r with { Y = r.Y - dy }, DrawImage i => i with { Y = i.Y - dy },
|
DrawRectangle r => r with { Y = r.Y - dy }, DrawRoundedRectangle r => r with { Y = r.Y - dy },
|
||||||
|
DrawImage i => i with { Y = i.Y - dy },
|
||||||
DrawLine ln => ln with { Y1 = ln.Y1 - dy, Y2 = ln.Y2 - dy },
|
DrawLine ln => ln with { Y1 = ln.Y1 - dy, Y2 = ln.Y2 - dy },
|
||||||
|
DrawCircle c => c with { CenterY = c.CenterY - dy },
|
||||||
_ => command,
|
_ => command,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -649,6 +652,19 @@ internal static class DrawingElementRenderer
|
|||||||
svg.Append(CultureInfo.InvariantCulture,
|
svg.Append(CultureInfo.InvariantCulture,
|
||||||
$"<rect x=\"{rectangle.X}\" y=\"{rectangle.Y}\" width=\"{rectangle.Width}\" height=\"{rectangle.Height}\" stroke=\"{Attribute(rectangle.StrokeColor)}\" stroke-width=\"{rectangle.StrokeWidth}\" fill=\"{Attribute(rectangle.FillColor)}\"/>");
|
$"<rect x=\"{rectangle.X}\" y=\"{rectangle.Y}\" width=\"{rectangle.Width}\" height=\"{rectangle.Height}\" stroke=\"{Attribute(rectangle.StrokeColor)}\" stroke-width=\"{rectangle.StrokeWidth}\" fill=\"{Attribute(rectangle.FillColor)}\"/>");
|
||||||
break;
|
break;
|
||||||
|
case DrawRoundedRectangle rectangle:
|
||||||
|
Validate(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height, rectangle.CornerRadius,
|
||||||
|
rectangle.StrokeWidth);
|
||||||
|
Positive(rectangle.Width, rectangle.Height, rectangle.CornerRadius, rectangle.StrokeWidth);
|
||||||
|
svg.Append(CultureInfo.InvariantCulture,
|
||||||
|
$"<rect x=\"{rectangle.X}\" y=\"{rectangle.Y}\" width=\"{rectangle.Width}\" height=\"{rectangle.Height}\" rx=\"{rectangle.CornerRadius}\" ry=\"{rectangle.CornerRadius}\" stroke=\"{Attribute(rectangle.StrokeColor)}\" stroke-width=\"{rectangle.StrokeWidth}\" fill=\"{Attribute(rectangle.FillColor)}\"/>");
|
||||||
|
break;
|
||||||
|
case DrawCircle circle:
|
||||||
|
Validate(circle.CenterX, circle.CenterY, circle.Radius, circle.StrokeWidth);
|
||||||
|
Positive(circle.Radius, circle.StrokeWidth);
|
||||||
|
svg.Append(CultureInfo.InvariantCulture,
|
||||||
|
$"<circle cx=\"{circle.CenterX}\" cy=\"{circle.CenterY}\" r=\"{circle.Radius}\" stroke=\"{Attribute(circle.StrokeColor)}\" stroke-width=\"{circle.StrokeWidth}\" fill=\"{Attribute(circle.FillColor)}\"/>");
|
||||||
|
break;
|
||||||
case DrawString text:
|
case DrawString text:
|
||||||
Validate(text.X, text.Y, text.FontSize);
|
Validate(text.X, text.Y, text.FontSize);
|
||||||
Positive(text.FontSize);
|
Positive(text.FontSize);
|
||||||
|
|||||||
Reference in New Issue
Block a user