Skip to content

Commit 952fc98

Browse files
committed
added EllipticalArcLineSegment
1 parent 423bf06 commit 952fc98

1 file changed

Lines changed: 132 additions & 0 deletions

File tree

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Numerics;
7+
8+
namespace SixLabors.ImageSharp.Drawing.Shapes
9+
{
10+
/// <summary>
11+
/// Represents a line segment that contains radii and angles that will be rendered as a elliptical arc
12+
/// </summary>
13+
/// <seealso cref="ILineSegment" />
14+
public sealed class EllipticalArcLineSegment : ILineSegment
15+
{
16+
private const float MinimumSqrDistance = 1.75f;
17+
private readonly PointF[] linePoints;
18+
private PointF center;
19+
private float firstRadius;
20+
private float secondRadius;
21+
private float rotation;
22+
private float startAngle;
23+
private float sweepAngle;
24+
25+
/// <summary>
26+
/// Initializes a new instance of the <see cref="EllipticalArcLineSegment"/> class.
27+
/// </summary>
28+
/// <param name="center"> The center point of the ellipsis that the arc is a part of</param>
29+
/// <param name="firstRadius">First radius of the ellipsis</param>
30+
/// <param name="secondRadius">Second radius of the ellipsis</param>
31+
/// <param name="rotation">The rotation of First radius to the X-Axis</param>
32+
/// <param name="startAngle">The Start angle of the ellipsis</param>
33+
/// <param name="sweepAngle"> The sweeping angle of the arc</param>
34+
public EllipticalArcLineSegment(PointF center, float firstRadius, float secondRadius, float rotation, float startAngle, float sweepAngle)
35+
{
36+
Guard.MustBeGreaterThan(firstRadius, 0, nameof(firstRadius));
37+
Guard.MustBeGreaterThan(secondRadius, 0, nameof(secondRadius));
38+
Guard.MustBeBetweenOrEqualTo(rotation, 0, 360, nameof(rotation));
39+
Guard.MustBeBetweenOrEqualTo(startAngle, 0, 360, nameof(startAngle));
40+
Guard.MustBeBetweenOrEqualTo(sweepAngle, 0, 360, nameof(sweepAngle));
41+
this.center = center;
42+
this.firstRadius = firstRadius;
43+
this.secondRadius = secondRadius;
44+
this.rotation = rotation;
45+
this.startAngle = startAngle;
46+
this.sweepAngle = sweepAngle;
47+
this.linePoints = this.GetDrawingPoints();
48+
this.EndPoint = this.linePoints[this.linePoints.Length - 1];
49+
}
50+
51+
/// <summary>
52+
/// Gets the end point.
53+
/// </summary>
54+
/// <value>
55+
/// The end point.
56+
/// </value>
57+
public PointF EndPoint { get; }
58+
59+
/// <summary>
60+
/// Transforms the current LineSegment using specified matrix.
61+
/// </summary>
62+
/// <param name="matrix">The matrix.</param>
63+
/// <returns>A line segment with the matrix applied to it.</returns>
64+
public EllipticalArcLineSegment Transform(Matrix3x2 matrix)
65+
{
66+
if (matrix.IsIdentity)
67+
{
68+
return this;
69+
}
70+
71+
// TODO
72+
}
73+
74+
/// <summary>
75+
/// Transforms the current LineSegment using specified matrix.
76+
/// </summary>
77+
/// <param name="matrix">The matrix.</param>
78+
/// <returns>A line segment with the matrix applied to it.</returns>
79+
ILineSegment ILineSegment.Transform(Matrix3x2 matrix) => this.Transform(matrix);
80+
81+
private PointF[] GetDrawingPoints()
82+
{
83+
var points = new List<PointF>();
84+
85+
float startX = (float)((this.firstRadius * Math.Sin(Math.PI * this.startAngle / 180) * Math.Cos(Math.PI * this.rotation / 180)) - (this.secondRadius * Math.Cos(Math.PI * this.startAngle / 180) * Math.Sin(Math.PI * this.rotation / 180)) + this.center.X);
86+
float startY = (float)((this.firstRadius * Math.Sin(Math.PI * this.startAngle / 180) * Math.Sin(Math.PI * this.rotation / 180)) + (this.secondRadius * Math.Cos(Math.PI * this.startAngle / 180) * Math.Cos(Math.PI * this.rotation / 180)) + this.center.Y);
87+
points.Add(new PointF(startX, startY));
88+
for (float i = this.startAngle; i < this.startAngle + this.sweepAngle; i++)
89+
{
90+
float end = i + 1;
91+
if (end <= this.startAngle + this.sweepAngle)
92+
{
93+
end = this.startAngle + this.sweepAngle;
94+
}
95+
96+
points.AddRange(this.GetDrawingPoints(points[points.Count - 1], i, end));
97+
}
98+
99+
return points.ToArray();
100+
}
101+
102+
private List<PointF> GetDrawingPoints(PointF start, float startAngle, float end)
103+
{
104+
var points = new List<PointF>();
105+
float endX = (float)((this.firstRadius * Math.Sin(Math.PI * end / 180) * Math.Cos(Math.PI * this.rotation / 180)) - (this.secondRadius * Math.Cos(Math.PI * end / 180) * Math.Sin(Math.PI * this.rotation / 180)) + this.center.X);
106+
float endY = (float)((this.firstRadius * Math.Sin(Math.PI * end / 180) * Math.Sin(Math.PI * this.rotation / 180)) + (this.secondRadius * Math.Cos(Math.PI * end / 180) * Math.Cos(Math.PI * this.rotation / 180)) + this.center.Y);
107+
if ((new Vector2(endX, endY) - new Vector2(start.X, start.Y)).LengthSquared() < MinimumSqrDistance)
108+
{
109+
points.Add(new PointF(endX, endY));
110+
}
111+
else
112+
{
113+
float mid = startAngle + ((startAngle - end) / 2);
114+
points.AddRange(this.GetDrawingPoints(start, startAngle, mid));
115+
points.AddRange(this.GetDrawingPoints(points[points.Count - 1], mid, end));
116+
}
117+
118+
return points;
119+
}
120+
121+
/// <summary>
122+
/// Returns the current <see cref="ILineSegment" /> a simple linear path.
123+
/// </summary>
124+
/// <returns>
125+
/// Returns the current <see cref="ILineSegment" /> as simple linear path.
126+
/// </returns>
127+
public ReadOnlyMemory<PointF> Flatten()
128+
{
129+
return this.linePoints;
130+
}
131+
}
132+
}

0 commit comments

Comments
 (0)