|
using System;
namespace Microsoft.Maui.Graphics
{
internal class ArcFlattener
{
private float _cx;
private float _cy;
private float _diameter;
private float _radius;
private float _fx;
private float _fy;
private float _sweep;
private float _startAngle;
private PointF _startPoint;
public ArcFlattener(
float x,
float y,
float width,
float height,
float startAngle,
float endAngle,
bool clockwise)
{
_startAngle = startAngle;
_cx = x + (width / 2);
_cy = y + (height / 2);
_diameter = Math.Max(width, height);
_radius = _diameter / 2;
_fx = width / _diameter;
_fy = height / _diameter;
_sweep = Math.Abs(endAngle - startAngle);
if (clockwise)
_sweep *= -1;
_startPoint = GetPointOnArc(0);
}
private PointF GetPointOnArc(
float percentage)
{
var angle = _startAngle + (_sweep * percentage);
while (angle >= 360)
angle -= 360;
angle *= -1;
var radians = (float)GeometryUtil.DegreesToRadians(angle);
var point = GetPointAtAngle(0, 0, _radius, radians);
point.X = _cx + (point.X * _fx);
point.Y = _cy + (point.Y * _fy);
return point;
}
private static PointF GetPointAtAngle(
float x,
float y,
float distance,
float radians)
{
var x2 = x + (Math.Cos(radians) * distance);
var y2 = y + (Math.Sin(radians) * distance);
return new PointF((float)x2, (float)y2);
}
private static PointF GetCenter(PointF point1, PointF point2)
{
var x = (point1.X + point2.X) / 2;
var y = (point1.Y + point2.Y) / 2;
return new PointF(x, y);
}
public PathF CreateFlattenedPath(
float flatness = .5f)
{
var found = false;
var n = 1;
PointF? endPoint = null;
while ((!found) && (n < 1024))
{
var candidate = 1f / (float)n;
var midPointOnArc = GetPointOnArc(candidate / 2);
if (endPoint == null)
endPoint = GetPointOnArc(candidate);
var midPointOnLine = GetCenter(_startPoint, (PointF)endPoint);
if (GeometryUtil.GetDistance(midPointOnArc.X, midPointOnArc.Y, midPointOnLine.X, midPointOnLine.Y) <= flatness)
{
found = true;
n = n << 1;
}
else
{
endPoint = midPointOnArc;
n++;
}
}
var path = new PathF();
path.MoveTo(_startPoint);
float step = 1f / n;
float percentage = 0;
for (var i = 1; i < n; i++)
{
percentage += step;
var point = GetPointOnArc(percentage);
path.LineTo(point);
}
path.LineTo(GetPointOnArc(1));
return path;
}
}
}
|