Skip to content

Commit 5077d42

Browse files
Add PathBuilder shape helpers; normalize angles to degrees
1 parent aba2197 commit 5077d42

File tree

7 files changed

+352
-25
lines changed

7 files changed

+352
-25
lines changed

src/ImageSharp.Drawing/PathBuilder.cs

Lines changed: 230 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public PathBuilder(Matrix4x4 defaultTransform)
3434
{
3535
this.defaultTransform = defaultTransform;
3636
this.Clear();
37-
this.ResetTransform();
37+
_ = this.ResetTransform();
3838
}
3939

4040
/// <summary>
@@ -105,7 +105,7 @@ public PathBuilder ResetOrigin()
105105
/// <returns>The <see cref="PathBuilder"/>.</returns>
106106
public PathBuilder MoveTo(PointF point)
107107
{
108-
this.StartFigure();
108+
_ = this.StartFigure();
109109
this.currentPoint = PointF.Transform(point, this.currentTransform);
110110
return this;
111111
}
@@ -387,6 +387,231 @@ public PathBuilder AddArc(int x, int y, int radiusX, int radiusY, int rotation,
387387
public PathBuilder AddArc(float x, float y, float radiusX, float radiusY, float rotation, float startAngle, float sweepAngle)
388388
=> this.AddSegment(new ArcLineSegment(new PointF(x, y), new SizeF(radiusX, radiusY), rotation, startAngle, sweepAngle));
389389

390+
/// <summary>
391+
/// Adds a pie sector to the current path as a closed figure.
392+
/// </summary>
393+
/// <param name="center">The center point of the pie.</param>
394+
/// <param name="radius">The x and y radii of the pie ellipse.</param>
395+
/// <param name="rotation">The ellipse rotation in degrees.</param>
396+
/// <param name="startAngle">The pie start angle in degrees.</param>
397+
/// <param name="sweepAngle">The pie sweep angle in degrees.</param>
398+
/// <returns>The <see cref="PathBuilder"/>.</returns>
399+
public PathBuilder AddPie(PointF center, SizeF radius, float rotation, float startAngle, float sweepAngle)
400+
{
401+
_ = this.StartFigure();
402+
403+
foreach (ILineSegment segment in new Pie(center, radius, rotation, startAngle, sweepAngle).LineSegments)
404+
{
405+
_ = this.AddSegment(segment);
406+
}
407+
408+
return this.CloseFigure();
409+
}
410+
411+
/// <summary>
412+
/// Adds a pie sector to the current path as a closed figure.
413+
/// </summary>
414+
/// <param name="center">The center point of the pie.</param>
415+
/// <param name="radius">The x and y radii of the pie ellipse.</param>
416+
/// <param name="startAngle">The pie start angle in degrees.</param>
417+
/// <param name="sweepAngle">The pie sweep angle in degrees.</param>
418+
/// <returns>The <see cref="PathBuilder"/>.</returns>
419+
public PathBuilder AddPie(PointF center, SizeF radius, float startAngle, float sweepAngle)
420+
=> this.AddPie(center, radius, 0F, startAngle, sweepAngle);
421+
422+
/// <summary>
423+
/// Adds a pie sector to the current path as a closed figure.
424+
/// </summary>
425+
/// <param name="x">The x-coordinate of the pie center.</param>
426+
/// <param name="y">The y-coordinate of the pie center.</param>
427+
/// <param name="radiusX">The x-radius of the pie ellipse.</param>
428+
/// <param name="radiusY">The y-radius of the pie ellipse.</param>
429+
/// <param name="rotation">The ellipse rotation in degrees.</param>
430+
/// <param name="startAngle">The pie start angle in degrees.</param>
431+
/// <param name="sweepAngle">The pie sweep angle in degrees.</param>
432+
/// <returns>The <see cref="PathBuilder"/>.</returns>
433+
public PathBuilder AddPie(float x, float y, float radiusX, float radiusY, float rotation, float startAngle, float sweepAngle)
434+
=> this.AddPie(new PointF(x, y), new SizeF(radiusX, radiusY), rotation, startAngle, sweepAngle);
435+
436+
/// <summary>
437+
/// Adds a pie sector to the current path as a closed figure.
438+
/// </summary>
439+
/// <param name="x">The x-coordinate of the pie center.</param>
440+
/// <param name="y">The y-coordinate of the pie center.</param>
441+
/// <param name="radiusX">The x-radius of the pie ellipse.</param>
442+
/// <param name="radiusY">The y-radius of the pie ellipse.</param>
443+
/// <param name="startAngle">The pie start angle in degrees.</param>
444+
/// <param name="sweepAngle">The pie sweep angle in degrees.</param>
445+
/// <returns>The <see cref="PathBuilder"/>.</returns>
446+
public PathBuilder AddPie(float x, float y, float radiusX, float radiusY, float startAngle, float sweepAngle)
447+
=> this.AddPie(x, y, radiusX, radiusY, 0F, startAngle, sweepAngle);
448+
449+
/// <summary>
450+
/// Adds a rectangle to the current path as a closed figure.
451+
/// </summary>
452+
/// <param name="rectangle">The rectangle bounds.</param>
453+
/// <returns>The <see cref="PathBuilder"/>.</returns>
454+
public PathBuilder AddRectangle(RectangleF rectangle)
455+
=> this.AddRectangle(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
456+
457+
/// <summary>
458+
/// Adds a rectangle to the current path as a closed figure.
459+
/// </summary>
460+
/// <param name="rectangle">The rectangle bounds.</param>
461+
/// <returns>The <see cref="PathBuilder"/>.</returns>
462+
public PathBuilder AddRectangle(Rectangle rectangle)
463+
=> this.AddRectangle((RectangleF)rectangle);
464+
465+
/// <summary>
466+
/// Adds a rectangle to the current path as a closed figure.
467+
/// </summary>
468+
/// <param name="x">The x-coordinate of the rectangle.</param>
469+
/// <param name="y">The y-coordinate of the rectangle.</param>
470+
/// <param name="width">The rectangle width.</param>
471+
/// <param name="height">The rectangle height.</param>
472+
/// <returns>The <see cref="PathBuilder"/>.</returns>
473+
public PathBuilder AddRectangle(float x, float y, float width, float height)
474+
=> this.AddPolygon(
475+
new PointF(x, y),
476+
new PointF(x + width, y),
477+
new PointF(x + width, y + height),
478+
new PointF(x, y + height));
479+
480+
/// <summary>
481+
/// Adds a polygon to the current path as a closed figure.
482+
/// </summary>
483+
/// <param name="points">The polygon vertices.</param>
484+
/// <returns>The <see cref="PathBuilder"/>.</returns>
485+
public PathBuilder AddPolygon(IEnumerable<PointF> points)
486+
{
487+
Guard.NotNull(points, nameof(points));
488+
return this.AddPolygon([.. points]);
489+
}
490+
491+
/// <summary>
492+
/// Adds a polygon to the current path as a closed figure.
493+
/// </summary>
494+
/// <param name="points">The polygon vertices.</param>
495+
/// <returns>The <see cref="PathBuilder"/>.</returns>
496+
public PathBuilder AddPolygon(params PointF[] points)
497+
{
498+
Guard.NotNull(points, nameof(points));
499+
500+
_ = this.StartFigure();
501+
_ = this.AddSegment(new LinearLineSegment(points));
502+
return this.CloseFigure();
503+
}
504+
505+
/// <summary>
506+
/// Adds a regular polygon to the current path as a closed figure.
507+
/// </summary>
508+
/// <param name="center">The center point of the polygon.</param>
509+
/// <param name="vertices">The number of polygon vertices.</param>
510+
/// <param name="radius">The polygon radius.</param>
511+
/// <returns>The <see cref="PathBuilder"/>.</returns>
512+
public PathBuilder AddRegularPolygon(PointF center, int vertices, float radius)
513+
=> this.AddRegularPolygon(center, vertices, radius, 0F);
514+
515+
/// <summary>
516+
/// Adds a regular polygon to the current path as a closed figure.
517+
/// </summary>
518+
/// <param name="center">The center point of the polygon.</param>
519+
/// <param name="vertices">The number of polygon vertices.</param>
520+
/// <param name="radius">The polygon radius.</param>
521+
/// <param name="angle">The polygon rotation angle in degrees.</param>
522+
/// <returns>The <see cref="PathBuilder"/>.</returns>
523+
public PathBuilder AddRegularPolygon(PointF center, int vertices, float radius, float angle)
524+
{
525+
_ = this.StartFigure();
526+
527+
foreach (ILineSegment segment in new RegularPolygon(center, vertices, radius, angle).LineSegments)
528+
{
529+
_ = this.AddSegment(segment);
530+
}
531+
532+
return this.CloseFigure();
533+
}
534+
535+
/// <summary>
536+
/// Adds a regular polygon to the current path as a closed figure.
537+
/// </summary>
538+
/// <param name="x">The x-coordinate of the polygon center.</param>
539+
/// <param name="y">The y-coordinate of the polygon center.</param>
540+
/// <param name="vertices">The number of polygon vertices.</param>
541+
/// <param name="radius">The polygon radius.</param>
542+
/// <returns>The <see cref="PathBuilder"/>.</returns>
543+
public PathBuilder AddRegularPolygon(float x, float y, int vertices, float radius)
544+
=> this.AddRegularPolygon(new PointF(x, y), vertices, radius);
545+
546+
/// <summary>
547+
/// Adds a regular polygon to the current path as a closed figure.
548+
/// </summary>
549+
/// <param name="x">The x-coordinate of the polygon center.</param>
550+
/// <param name="y">The y-coordinate of the polygon center.</param>
551+
/// <param name="vertices">The number of polygon vertices.</param>
552+
/// <param name="radius">The polygon radius.</param>
553+
/// <param name="angle">The polygon rotation angle in degrees.</param>
554+
/// <returns>The <see cref="PathBuilder"/>.</returns>
555+
public PathBuilder AddRegularPolygon(float x, float y, int vertices, float radius, float angle)
556+
=> this.AddRegularPolygon(new PointF(x, y), vertices, radius, angle);
557+
558+
/// <summary>
559+
/// Adds a star to the current path as a closed figure.
560+
/// </summary>
561+
/// <param name="center">The center point of the star.</param>
562+
/// <param name="prongs">The number of star prongs.</param>
563+
/// <param name="innerRadii">The inner star radius.</param>
564+
/// <param name="outerRadii">The outer star radius.</param>
565+
/// <returns>The <see cref="PathBuilder"/>.</returns>
566+
public PathBuilder AddStar(PointF center, int prongs, float innerRadii, float outerRadii)
567+
=> this.AddStar(center, prongs, innerRadii, outerRadii, 0F);
568+
569+
/// <summary>
570+
/// Adds a star to the current path as a closed figure.
571+
/// </summary>
572+
/// <param name="center">The center point of the star.</param>
573+
/// <param name="prongs">The number of star prongs.</param>
574+
/// <param name="innerRadii">The inner star radius.</param>
575+
/// <param name="outerRadii">The outer star radius.</param>
576+
/// <param name="angle">The star rotation angle in degrees.</param>
577+
/// <returns>The <see cref="PathBuilder"/>.</returns>
578+
public PathBuilder AddStar(PointF center, int prongs, float innerRadii, float outerRadii, float angle)
579+
{
580+
_ = this.StartFigure();
581+
582+
foreach (ILineSegment segment in new Star(center, prongs, innerRadii, outerRadii, angle).LineSegments)
583+
{
584+
_ = this.AddSegment(segment);
585+
}
586+
587+
return this.CloseFigure();
588+
}
589+
590+
/// <summary>
591+
/// Adds a star to the current path as a closed figure.
592+
/// </summary>
593+
/// <param name="x">The x-coordinate of the star center.</param>
594+
/// <param name="y">The y-coordinate of the star center.</param>
595+
/// <param name="prongs">The number of star prongs.</param>
596+
/// <param name="innerRadii">The inner star radius.</param>
597+
/// <param name="outerRadii">The outer star radius.</param>
598+
/// <returns>The <see cref="PathBuilder"/>.</returns>
599+
public PathBuilder AddStar(float x, float y, int prongs, float innerRadii, float outerRadii)
600+
=> this.AddStar(new PointF(x, y), prongs, innerRadii, outerRadii);
601+
602+
/// <summary>
603+
/// Adds a star to the current path as a closed figure.
604+
/// </summary>
605+
/// <param name="x">The x-coordinate of the star center.</param>
606+
/// <param name="y">The y-coordinate of the star center.</param>
607+
/// <param name="prongs">The number of star prongs.</param>
608+
/// <param name="innerRadii">The inner star radius.</param>
609+
/// <param name="outerRadii">The outer star radius.</param>
610+
/// <param name="angle">The star rotation angle in degrees.</param>
611+
/// <returns>The <see cref="PathBuilder"/>.</returns>
612+
public PathBuilder AddStar(float x, float y, int prongs, float innerRadii, float outerRadii, float angle)
613+
=> this.AddStar(new PointF(x, y), prongs, innerRadii, outerRadii, angle);
614+
390615
/// <summary>
391616
/// Starts a new figure but leaves the previous one open.
392617
/// </summary>
@@ -413,7 +638,7 @@ public PathBuilder StartFigure()
413638
public PathBuilder CloseFigure()
414639
{
415640
this.currentFigure.IsClosed = true;
416-
this.StartFigure();
641+
_ = this.StartFigure();
417642

418643
return this;
419644
}
@@ -429,7 +654,7 @@ public PathBuilder CloseAllFigures()
429654
f.IsClosed = true;
430655
}
431656

432-
this.CloseFigure();
657+
_ = this.CloseFigure();
433658

434659
return this;
435660
}
@@ -456,7 +681,7 @@ public IPath Build()
456681
public PathBuilder Reset()
457682
{
458683
this.Clear();
459-
this.ResetTransform();
684+
_ = this.ResetTransform();
460685
this.currentPoint = default;
461686

462687
return this;

src/ImageSharp.Drawing/RegularPolygon.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class RegularPolygon : Polygon
1616
/// <param name="location">The location the center of the polygon will be placed.</param>
1717
/// <param name="vertices">The number of vertices the <see cref="RegularPolygon"/> should have.</param>
1818
/// <param name="radius">The radius of the circle that would touch all vertices.</param>
19-
/// <param name="angle">The angle of rotation in Radians</param>
19+
/// <param name="angle">The angle of rotation in degrees.</param>
2020
public RegularPolygon(PointF location, int vertices, float radius, float angle)
2121
: base(CreateSegment(location, radius, vertices, angle))
2222
{
@@ -40,7 +40,7 @@ public RegularPolygon(PointF location, int vertices, float radius)
4040
/// <param name="y">The y-coordinate of the center of the polygon.</param>
4141
/// <param name="vertices">The number of vertices the <see cref="RegularPolygon" /> should have.</param>
4242
/// <param name="radius">The radius of the circle that would touch all vertices.</param>
43-
/// <param name="angle">The angle of rotation in Radians</param>
43+
/// <param name="angle">The angle of rotation in degrees.</param>
4444
public RegularPolygon(float x, float y, int vertices, float radius, float angle)
4545
: this(new PointF(x, y), vertices, radius, angle)
4646
{
@@ -66,7 +66,7 @@ private static LinearLineSegment CreateSegment(PointF location, float radius, in
6666
PointF distanceVector = new(0, radius);
6767

6868
float anglePerSegments = (float)(2 * Math.PI / vertices);
69-
float current = angle;
69+
float current = GeometryUtilities.DegreeToRadian(angle);
7070
PointF[] points = new PointF[vertices];
7171
for (int i = 0; i < vertices; i++)
7272
{

src/ImageSharp.Drawing/Star.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public sealed class Star : Polygon
1717
/// <param name="prongs">The number of points the <see cref="Star" /> should have.</param>
1818
/// <param name="innerRadii">The inner radii.</param>
1919
/// <param name="outerRadii">The outer radii.</param>
20-
/// <param name="angle">The angle of rotation in Radians</param>
20+
/// <param name="angle">The angle of rotation in degrees.</param>
2121
public Star(PointF location, int prongs, float innerRadii, float outerRadii, float angle)
2222
: base(CreateSegment(location, innerRadii, outerRadii, prongs, angle))
2323
{
@@ -43,7 +43,7 @@ public Star(PointF location, int prongs, float innerRadii, float outerRadii)
4343
/// <param name="prongs">The number of vertices the <see cref="RegularPolygon" /> should have.</param>
4444
/// <param name="innerRadii">The inner radii.</param>
4545
/// <param name="outerRadii">The outer radii.</param>
46-
/// <param name="angle">The angle of rotation in Radians</param>
46+
/// <param name="angle">The angle of rotation in degrees.</param>
4747
public Star(float x, float y, int prongs, float innerRadii, float outerRadii, float angle)
4848
: this(new PointF(x, y), prongs, innerRadii, outerRadii, angle)
4949
{
@@ -73,7 +73,7 @@ private static LinearLineSegment CreateSegment(Vector2 location, float innerRadi
7373

7474
int vertices = prongs * 2;
7575
float anglePerSegments = (float)(2 * Math.PI / vertices);
76-
float current = angle;
76+
float current = GeometryUtilities.DegreeToRadian(angle);
7777
PointF[] points = new PointF[vertices];
7878
Vector2 distance = distanceVectorInner;
7979
for (int i = 0; i < vertices; i++)

tests/ImageSharp.Drawing.Tests/Processing/ProcessWithDrawingCanvasTests.Polygons.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -325,8 +325,7 @@ public void FillPolygon_RectangularPolygon<TPixel>(TestImageProvider<TPixel> pro
325325
public void FillPolygon_RegularPolygon<TPixel>(TestImageProvider<TPixel> provider, int vertices, float radius, float angleDeg)
326326
where TPixel : unmanaged, IPixel<TPixel>
327327
{
328-
float angle = GeometryUtilities.DegreeToRadian(angleDeg);
329-
RegularPolygon polygon = new(100, 100, vertices, radius, angle);
328+
RegularPolygon polygon = new(100, 100, vertices, radius, angleDeg);
330329
Color color = Color.Yellow;
331330

332331
FormattableString testOutput = $"V({vertices})_R({radius})_Ang({angleDeg})";

0 commit comments

Comments
 (0)