Skip to content

Commit 5553d94

Browse files
Add Fill symmetry, new Pie polygon, and migrate to extensions
1 parent 44d0e74 commit 5553d94

File tree

9 files changed

+478
-127
lines changed

9 files changed

+478
-127
lines changed

src/ImageSharp.Drawing/Pie.cs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
using System.Numerics;
5+
6+
namespace SixLabors.ImageSharp.Drawing;
7+
8+
/// <summary>
9+
/// A pie sector shape defined by a center point, radii, rotation, and arc sweep.
10+
/// </summary>
11+
public sealed class Pie : Polygon
12+
{
13+
/// <summary>
14+
/// Initializes a new instance of the <see cref="Pie"/> class.
15+
/// </summary>
16+
/// <param name="center">The center point of the pie.</param>
17+
/// <param name="radius">The x and y radii of the pie ellipse.</param>
18+
/// <param name="rotation">The ellipse rotation in degrees.</param>
19+
/// <param name="startAngle">The pie start angle in degrees.</param>
20+
/// <param name="sweepAngle">The pie sweep angle in degrees.</param>
21+
public Pie(PointF center, SizeF radius, float rotation, float startAngle, float sweepAngle)
22+
: base(CreateSegments(center, radius, rotation, startAngle, sweepAngle))
23+
{
24+
}
25+
26+
/// <summary>
27+
/// Initializes a new instance of the <see cref="Pie"/> class.
28+
/// </summary>
29+
/// <param name="center">The center point of the pie.</param>
30+
/// <param name="radius">The x and y radii of the pie ellipse.</param>
31+
/// <param name="startAngle">The pie start angle in degrees.</param>
32+
/// <param name="sweepAngle">The pie sweep angle in degrees.</param>
33+
public Pie(PointF center, SizeF radius, float startAngle, float sweepAngle)
34+
: this(center, radius, 0F, startAngle, sweepAngle)
35+
{
36+
}
37+
38+
/// <summary>
39+
/// Initializes a new instance of the <see cref="Pie"/> class.
40+
/// </summary>
41+
/// <param name="x">The x-coordinate of the pie center.</param>
42+
/// <param name="y">The y-coordinate of the pie center.</param>
43+
/// <param name="radiusX">The x-radius of the pie ellipse.</param>
44+
/// <param name="radiusY">The y-radius of the pie ellipse.</param>
45+
/// <param name="rotation">The ellipse rotation in degrees.</param>
46+
/// <param name="startAngle">The pie start angle in degrees.</param>
47+
/// <param name="sweepAngle">The pie sweep angle in degrees.</param>
48+
public Pie(float x, float y, float radiusX, float radiusY, float rotation, float startAngle, float sweepAngle)
49+
: this(new PointF(x, y), new SizeF(radiusX, radiusY), rotation, startAngle, sweepAngle)
50+
{
51+
}
52+
53+
/// <summary>
54+
/// Initializes a new instance of the <see cref="Pie"/> class.
55+
/// </summary>
56+
/// <param name="x">The x-coordinate of the pie center.</param>
57+
/// <param name="y">The y-coordinate of the pie center.</param>
58+
/// <param name="radiusX">The x-radius of the pie ellipse.</param>
59+
/// <param name="radiusY">The y-radius of the pie ellipse.</param>
60+
/// <param name="startAngle">The pie start angle in degrees.</param>
61+
/// <param name="sweepAngle">The pie sweep angle in degrees.</param>
62+
public Pie(float x, float y, float radiusX, float radiusY, float startAngle, float sweepAngle)
63+
: this(x, y, radiusX, radiusY, 0F, startAngle, sweepAngle)
64+
{
65+
}
66+
67+
private Pie(ILineSegment[] segments)
68+
: base(segments, true)
69+
{
70+
}
71+
72+
/// <inheritdoc />
73+
public override IPath Transform(Matrix4x4 matrix)
74+
{
75+
if (matrix.IsIdentity)
76+
{
77+
return this;
78+
}
79+
80+
ILineSegment[] segments = new ILineSegment[this.LineSegments.Count];
81+
82+
for (int i = 0; i < segments.Length; i++)
83+
{
84+
segments[i] = this.LineSegments[i].Transform(matrix);
85+
}
86+
87+
return new Pie(segments);
88+
}
89+
90+
private static ILineSegment[] CreateSegments(PointF center, SizeF radius, float rotation, float startAngle, float sweepAngle)
91+
{
92+
Guard.MustBeGreaterThan(radius.Width, 0, "radiusX");
93+
Guard.MustBeGreaterThan(radius.Height, 0, "radiusY");
94+
95+
PointF arcStart = GetArcPoint(center, radius, rotation, startAngle);
96+
ArcLineSegment arc = new(center, radius, rotation, startAngle, sweepAngle);
97+
98+
return
99+
[
100+
new LinearLineSegment(center, arcStart),
101+
arc,
102+
new LinearLineSegment(arc.EndPoint, center)
103+
];
104+
}
105+
106+
private static PointF GetArcPoint(PointF center, SizeF radius, float rotation, float angle)
107+
{
108+
float rotationRadians = rotation * (MathF.PI / 180F);
109+
float angleRadians = angle * (MathF.PI / 180F);
110+
float cosRotation = MathF.Cos(rotationRadians);
111+
float sinRotation = MathF.Sin(rotationRadians);
112+
float cosAngle = MathF.Cos(angleRadians);
113+
float sinAngle = MathF.Sin(angleRadians);
114+
115+
return new PointF(
116+
center.X + (radius.Width * cosRotation * cosAngle) - (radius.Height * sinRotation * sinAngle),
117+
center.Y + (radius.Width * sinRotation * cosAngle) + (radius.Height * cosRotation * sinAngle));
118+
}
119+
}
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
namespace SixLabors.ImageSharp.Drawing.Processing;
5+
6+
/// <summary>
7+
/// Convenience shape helpers that build paths and forward to the core <see cref="IDrawingCanvas"/> fill and draw primitives.
8+
/// </summary>
9+
public static class DrawingCanvasShapeExtensions
10+
{
11+
/// <summary>
12+
/// Fills a local region using the given brush.
13+
/// </summary>
14+
/// <param name="canvas">The destination canvas.</param>
15+
/// <param name="brush">Brush used to shade destination pixels.</param>
16+
/// <param name="region">Region to fill in local coordinates.</param>
17+
public static void Fill(this IDrawingCanvas canvas, Brush brush, Rectangle region)
18+
=> canvas.Fill(brush, new RectangularPolygon(region.X, region.Y, region.Width, region.Height));
19+
20+
/// <summary>
21+
/// Fills all paths in a collection using the given brush.
22+
/// </summary>
23+
/// <param name="canvas">The destination canvas.</param>
24+
/// <param name="brush">Brush used to shade covered pixels.</param>
25+
/// <param name="paths">Path collection to fill.</param>
26+
public static void Fill(this IDrawingCanvas canvas, Brush brush, IPathCollection paths)
27+
{
28+
Guard.NotNull(paths, nameof(paths));
29+
30+
foreach (IPath path in paths)
31+
{
32+
canvas.Fill(brush, path);
33+
}
34+
}
35+
36+
/// <summary>
37+
/// Fills a path built by the provided builder using the given brush.
38+
/// </summary>
39+
/// <param name="canvas">The destination canvas.</param>
40+
/// <param name="brush">Brush used to shade covered pixels.</param>
41+
/// <param name="pathBuilder">The path builder describing the fill region.</param>
42+
public static void Fill(this IDrawingCanvas canvas, Brush brush, PathBuilder pathBuilder)
43+
{
44+
Guard.NotNull(pathBuilder, nameof(pathBuilder));
45+
canvas.Fill(brush, pathBuilder.Build());
46+
}
47+
48+
/// <summary>
49+
/// Fills an ellipse using the provided brush.
50+
/// </summary>
51+
/// <param name="canvas">The destination canvas.</param>
52+
/// <param name="brush">Brush used to shade covered pixels.</param>
53+
/// <param name="center">Ellipse center point in local coordinates.</param>
54+
/// <param name="size">Ellipse width and height in local coordinates.</param>
55+
public static void FillEllipse(this IDrawingCanvas canvas, Brush brush, PointF center, SizeF size)
56+
=> canvas.Fill(brush, new EllipsePolygon(center, size));
57+
58+
/// <summary>
59+
/// Fills the closed arc shape produced by joining the arc endpoints with a straight line.
60+
/// </summary>
61+
/// <param name="canvas">The destination canvas.</param>
62+
/// <param name="brush">Brush used to shade covered pixels.</param>
63+
/// <param name="center">Arc center point in local coordinates.</param>
64+
/// <param name="radius">Arc radii in local coordinates.</param>
65+
/// <param name="rotation">Ellipse rotation in degrees.</param>
66+
/// <param name="startAngle">Arc start angle in degrees.</param>
67+
/// <param name="sweepAngle">Arc sweep angle in degrees.</param>
68+
public static void FillArc(this IDrawingCanvas canvas, Brush brush, PointF center, SizeF radius, float rotation, float startAngle, float sweepAngle)
69+
=> canvas.Fill(brush, new Path(new ArcLineSegment(center, radius, rotation, startAngle, sweepAngle)));
70+
71+
/// <summary>
72+
/// Fills a pie sector using the provided brush.
73+
/// </summary>
74+
/// <param name="canvas">The destination canvas.</param>
75+
/// <param name="brush">Brush used to shade covered pixels.</param>
76+
/// <param name="center">Pie center point in local coordinates.</param>
77+
/// <param name="radius">Pie radii in local coordinates.</param>
78+
/// <param name="rotation">Ellipse rotation in degrees.</param>
79+
/// <param name="startAngle">Pie start angle in degrees.</param>
80+
/// <param name="sweepAngle">Pie sweep angle in degrees.</param>
81+
public static void FillPie(this IDrawingCanvas canvas, Brush brush, PointF center, SizeF radius, float rotation, float startAngle, float sweepAngle)
82+
=> canvas.Fill(brush, new Pie(center, radius, rotation, startAngle, sweepAngle));
83+
84+
/// <summary>
85+
/// Fills a pie sector using the provided brush.
86+
/// </summary>
87+
/// <param name="canvas">The destination canvas.</param>
88+
/// <param name="brush">Brush used to shade covered pixels.</param>
89+
/// <param name="center">Pie center point in local coordinates.</param>
90+
/// <param name="radius">Pie radii in local coordinates.</param>
91+
/// <param name="startAngle">Pie start angle in degrees.</param>
92+
/// <param name="sweepAngle">Pie sweep angle in degrees.</param>
93+
public static void FillPie(this IDrawingCanvas canvas, Brush brush, PointF center, SizeF radius, float startAngle, float sweepAngle)
94+
=> canvas.Fill(brush, new Pie(center, radius, startAngle, sweepAngle));
95+
96+
/// <summary>
97+
/// Draws an arc outline using the provided pen.
98+
/// </summary>
99+
/// <param name="canvas">The destination canvas.</param>
100+
/// <param name="pen">Pen used to generate the arc outline.</param>
101+
/// <param name="center">Arc center point in local coordinates.</param>
102+
/// <param name="radius">Arc radii in local coordinates.</param>
103+
/// <param name="rotation">Ellipse rotation in degrees.</param>
104+
/// <param name="startAngle">Arc start angle in degrees.</param>
105+
/// <param name="sweepAngle">Arc sweep angle in degrees.</param>
106+
public static void DrawArc(this IDrawingCanvas canvas, Pen pen, PointF center, SizeF radius, float rotation, float startAngle, float sweepAngle)
107+
=> canvas.Draw(pen, new Path(new ArcLineSegment(center, radius, rotation, startAngle, sweepAngle)));
108+
109+
/// <summary>
110+
/// Draws a cubic bezier outline using the provided pen.
111+
/// </summary>
112+
/// <param name="canvas">The destination canvas.</param>
113+
/// <param name="pen">Pen used to generate the bezier outline.</param>
114+
/// <param name="points">Bezier control points.</param>
115+
public static void DrawBezier(this IDrawingCanvas canvas, Pen pen, params PointF[] points)
116+
{
117+
Guard.NotNull(points, nameof(points));
118+
canvas.Draw(pen, new Path(new CubicBezierLineSegment(points)));
119+
}
120+
121+
/// <summary>
122+
/// Draws an ellipse outline using the provided pen.
123+
/// </summary>
124+
/// <param name="canvas">The destination canvas.</param>
125+
/// <param name="pen">Pen used to generate the ellipse outline.</param>
126+
/// <param name="center">Ellipse center point in local coordinates.</param>
127+
/// <param name="size">Ellipse width and height in local coordinates.</param>
128+
public static void DrawEllipse(this IDrawingCanvas canvas, Pen pen, PointF center, SizeF size)
129+
=> canvas.Draw(pen, new EllipsePolygon(center, size));
130+
131+
/// <summary>
132+
/// Draws a pie sector outline using the provided pen.
133+
/// </summary>
134+
/// <param name="canvas">The destination canvas.</param>
135+
/// <param name="pen">Pen used to generate the pie outline.</param>
136+
/// <param name="center">Pie center point in local coordinates.</param>
137+
/// <param name="radius">Pie radii in local coordinates.</param>
138+
/// <param name="rotation">Ellipse rotation in degrees.</param>
139+
/// <param name="startAngle">Pie start angle in degrees.</param>
140+
/// <param name="sweepAngle">Pie sweep angle in degrees.</param>
141+
public static void DrawPie(this IDrawingCanvas canvas, Pen pen, PointF center, SizeF radius, float rotation, float startAngle, float sweepAngle)
142+
=> canvas.Draw(pen, new Pie(center, radius, rotation, startAngle, sweepAngle));
143+
144+
/// <summary>
145+
/// Draws a pie sector outline using the provided pen.
146+
/// </summary>
147+
/// <param name="canvas">The destination canvas.</param>
148+
/// <param name="pen">Pen used to generate the pie outline.</param>
149+
/// <param name="center">Pie center point in local coordinates.</param>
150+
/// <param name="radius">Pie radii in local coordinates.</param>
151+
/// <param name="startAngle">Pie start angle in degrees.</param>
152+
/// <param name="sweepAngle">Pie sweep angle in degrees.</param>
153+
public static void DrawPie(this IDrawingCanvas canvas, Pen pen, PointF center, SizeF radius, float startAngle, float sweepAngle)
154+
=> canvas.Draw(pen, new Pie(center, radius, startAngle, sweepAngle));
155+
156+
/// <summary>
157+
/// Draws a rectangular outline using the provided pen.
158+
/// </summary>
159+
/// <param name="canvas">The destination canvas.</param>
160+
/// <param name="pen">Pen used to generate the rectangle outline.</param>
161+
/// <param name="region">Rectangle region to stroke.</param>
162+
public static void Draw(this IDrawingCanvas canvas, Pen pen, Rectangle region)
163+
=> canvas.Draw(pen, new RectangularPolygon(region.X, region.Y, region.Width, region.Height));
164+
165+
/// <summary>
166+
/// Draws all paths in a collection using the provided pen.
167+
/// </summary>
168+
/// <param name="canvas">The destination canvas.</param>
169+
/// <param name="pen">Pen used to generate outlines.</param>
170+
/// <param name="paths">Path collection to stroke.</param>
171+
public static void Draw(this IDrawingCanvas canvas, Pen pen, IPathCollection paths)
172+
{
173+
Guard.NotNull(paths, nameof(paths));
174+
175+
foreach (IPath path in paths)
176+
{
177+
canvas.Draw(pen, path);
178+
}
179+
}
180+
181+
/// <summary>
182+
/// Draws a path outline built by the provided builder using the given pen.
183+
/// </summary>
184+
/// <param name="canvas">The destination canvas.</param>
185+
/// <param name="pen">Pen used to generate the outline fill path.</param>
186+
/// <param name="pathBuilder">The path builder describing the path to stroke.</param>
187+
public static void Draw(this IDrawingCanvas canvas, Pen pen, PathBuilder pathBuilder)
188+
{
189+
Guard.NotNull(pathBuilder, nameof(pathBuilder));
190+
canvas.Draw(pen, pathBuilder.Build());
191+
}
192+
}

src/ImageSharp.Drawing/Processing/DrawingCanvas{TPixel}.cs

Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -305,28 +305,7 @@ public void Clear(Brush brush, IPath path)
305305

306306
/// <inheritdoc />
307307
public void Fill(Brush brush)
308-
=> this.Fill(brush, this.Bounds);
309-
310-
/// <inheritdoc />
311-
public void Fill(Brush brush, Rectangle region)
312-
=> this.Fill(brush, new RectangularPolygon(region.X, region.Y, region.Width, region.Height));
313-
314-
/// <inheritdoc />
315-
public void Fill(Brush brush, IPathCollection paths)
316-
{
317-
Guard.NotNull(paths, nameof(paths));
318-
foreach (IPath path in paths)
319-
{
320-
this.EnqueueFillPath(brush, path);
321-
}
322-
}
323-
324-
/// <inheritdoc />
325-
public void Fill(Brush brush, PathBuilder pathBuilder)
326-
{
327-
Guard.NotNull(pathBuilder, nameof(pathBuilder));
328-
this.Fill(brush, pathBuilder.Build());
329-
}
308+
=> this.Fill(brush, new RectangularPolygon(this.Bounds.X, this.Bounds.Y, this.Bounds.Width, this.Bounds.Height));
330309

331310
/// <inheritdoc />
332311
public void Fill(Brush brush, IPath path)
@@ -396,21 +375,6 @@ public void Process(IPath path, Action<IImageProcessingContext> operation)
396375
state.ClipPaths);
397376
}
398377

399-
/// <inheritdoc />
400-
public void DrawArc(Pen pen, PointF center, SizeF radius, float rotation, float startAngle, float sweepAngle)
401-
=> this.Draw(pen, new Path(new ArcLineSegment(center, radius, rotation, startAngle, sweepAngle)));
402-
403-
/// <inheritdoc />
404-
public void DrawBezier(Pen pen, params PointF[] points)
405-
{
406-
Guard.NotNull(points, nameof(points));
407-
this.Draw(pen, new Path(new CubicBezierLineSegment(points)));
408-
}
409-
410-
/// <inheritdoc />
411-
public void DrawEllipse(Pen pen, PointF center, SizeF size)
412-
=> this.Draw(pen, new EllipsePolygon(center, size));
413-
414378
/// <summary>
415379
/// Draws a two-point line segment using the provided pen and drawing options.
416380
/// </summary>
@@ -488,27 +452,6 @@ public void DrawLine(Pen pen, params PointF[] points)
488452
this.PrepareStrokePolylineCompositionCore(points, pen.StrokeFill, effectiveOptions, pen);
489453
}
490454

491-
/// <inheritdoc />
492-
public void Draw(Pen pen, Rectangle region)
493-
=> this.Draw(pen, new RectangularPolygon(region.X, region.Y, region.Width, region.Height));
494-
495-
/// <inheritdoc />
496-
public void Draw(Pen pen, IPathCollection paths)
497-
{
498-
Guard.NotNull(paths, nameof(paths));
499-
foreach (IPath path in paths)
500-
{
501-
this.Draw(pen, path);
502-
}
503-
}
504-
505-
/// <inheritdoc />
506-
public void Draw(Pen pen, PathBuilder pathBuilder)
507-
{
508-
Guard.NotNull(pathBuilder, nameof(pathBuilder));
509-
this.Draw(pen, pathBuilder.Build());
510-
}
511-
512455
/// <inheritdoc />
513456
public void Draw(Pen pen, IPath path)
514457
{

0 commit comments

Comments
 (0)