using System;
using System.Collections.Generic;
using System.Drawing;
using System.Numerics;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using CommonUsage.Mathematics;
namespace CommonUsage.Geometries
{
public class CircularArc : AbstractGeometry
{
///
/// 以center为圆心、radius为半径,从angleStart逆时针转到angleEnd所构成的圆弧。direction表示圆弧走向。
///
///
///
///
///
/// 表示圆弧走向,1为angleStart到angleEnd,-1为angleEnd到angleStart
public CircularArc(Vector2 center, float radius, float angleStart, float angleEnd, int direction, Padding paddingType)
{
_center = center;
_radius = radius;
_angleStart = angleStart;
_angleEnd = angleEnd;
_direction = direction;
PaddingType = paddingType;
ChangeShape();
CalculateVisPoints();
}
public override void Visualize(Action processDot, Action processLine,
bool visExtendedPart = false)
{
lock (_visPoints)
{
if (visExtendedPart)
{
}
for (var i = 0; i < _visPoints.Length - 1; ++i)
{
if ((i == 0 || i == _visPoints.Length - 2) && !visExtendedPart) continue;
var color = Color.Red;
if (i == 0 || i == _visPoints.Length - 2) color = Color.Gray;
processLine(new VisLine(_visPoints[i], _visPoints[i + 1],
false, i == (_visPoints.Length - 1) / 2, color));
}
if (visExtendedPart)
{
}
}
}
public void SwitchSide()
{
(_angleStart, _angleEnd) = (_angleEnd, _angleStart);
ChangeShape();
CalculateVisPoints();
}
public float VisAngleResolution = 1;
public Vector2 Center
{
get => _center;
set
{
_center = value;
ChangeShape();
CalculateVisPoints();
}
}
public float Radius
{
get => _radius;
set
{
_radius = value;
ChangeShape();
CalculateVisPoints();
}
}
public float AngleStart
{
get => _angleStart;
set
{
_angleStart = value;
ChangeShape();
CalculateVisPoints();
}
}
public float AngleEnd
{
get => _angleEnd;
set
{
_angleEnd = value;
ChangeShape();
CalculateVisPoints();
}
}
public int Direction
{
get => _direction;
set
{
_direction = value;
ChangeShape();
CalculateVisPoints();
}
}
public float AngleRange => _totalTh;
public Vector2 PointStart => _center + new Vector2(_radius * (float)Math.Cos(_angleStart / 180 * Math.PI),
_radius * (float)Math.Sin(_angleStart / 180 * Math.PI));
public Vector2 PointEnd => _center + new Vector2(_radius * (float)Math.Cos(_angleEnd / 180 * Math.PI),
_radius * (float)Math.Sin(_angleEnd / 180 * Math.PI));
public Vector2 Src => _src;
public Vector2 Dst => _dst;
public float TangentSrc => _tangentSrc;
public float TangentDst => _tangentDst;
public override (Vector2 Pt, float Angle, float Bias, float Position) QueryTangentPoint(Vector2 point)
{
var queryTh = (float)(Math.Atan2(point.Y - _center.Y, point.X - _center.X) / Math.PI * 180);
var p = new Vector2();
var tangent = 0f;
var pd = 0f;
var bestBias = float.MaxValue;
if ((((int)PaddingType >> 4) & 0x1) == 1)
{
var (bias1, hPnt1, fd1) = CommonMath.Project2DLine(point, _beforeStartSrc, _src);
if (fd1 <= 1000)
{
p = hPnt1;
tangent = (_direction >= 0 ? _angleStart : _angleEnd) + 90 * _direction;
pd = fd1;
bestBias = bias1;
}
}
if (((int)PaddingType & 0x1) == 1)
{
var (bias2, hPnt2, fd2) = CommonMath.Project2DLine(point, _dst, _afterEndDst);
if (fd2 >= 0 && Math.Abs(bias2) < Math.Abs(bestBias))
{
p = hPnt2;
tangent = (_direction >= 0 ? _angleEnd : _angleStart) + 90 * _direction;
pd = _totalLen + fd2;
bestBias = bias2;
}
}
var th1 = _direction == 1 ? CommonMath.ThDiff(queryTh, _angleStart) : CommonMath.ThDiff(_angleEnd, queryTh);
// todo: urgent bug! should use better strategy to prevent sign problem
if (th1 < -55) th1 += 360;
var arcBias = (_radius - Vector2.Distance(point, _center)) * _direction;
if (Math.Abs(arcBias) < Math.Abs(bestBias))
{
p = _center + _radius * new Vector2((float)Math.Cos(queryTh / 180 * Math.PI),
(float)Math.Sin(queryTh / 180 * Math.PI));
tangent = queryTh + 90 * _direction;
pd = _radius * th1 / 180 * (float)Math.PI;
bestBias = arcBias;
}
return (p, tangent, bestBias, pd);
}
public override float QueryCurvature(float position)
{
// var theta = (float)(_angleEnd - position / _radius / Math.PI * 180f + Math.PI);
// return Vectoriel.FromAngleLen(theta, 1f / _radius);
return 1000f / _radius * _direction;
}
public override float Length()
{
return _totalLen;
}
private void CalculateVisPoints()
{
lock (_visPoints)
{
// todo: overlapping start and end is problematic
var ptCnt = (int)Math.Ceiling((_angleEnd + 360 - _angleStart) % 360 / VisAngleResolution);
_visPoints = new Vector2[ptCnt + 2];
var starting = _angleStart;
if (_direction == -1) starting = _angleEnd;
_visPoints[0] = _beforeStartSrc;
for (var j = 0; j < ptCnt; ++j)
{
var th = starting + j * VisAngleResolution * _direction;
var radAngle = (float)(th / 180f * Math.PI);
_visPoints[j + 1] = Center + new Vector2((float)Math.Cos(radAngle), (float)Math.Sin(radAngle)) * Radius;
}
_visPoints[ptCnt + 1] = _afterEndDst;
}
}
private void ChangeShape()
{
_totalTh = CommonMath.ThDiff(_angleEnd, _angleStart);
if (_totalTh < 0) _totalTh += 360;
_totalLen = _radius * _totalTh / 180 * (float)Math.PI;
var radAngleStart = _angleStart / 180 * Math.PI;
var radAngleEnd = _angleEnd / 180 * Math.PI;
double srcAngle = radAngleStart, dstAngle = radAngleEnd;
if (_direction == -1) (srcAngle, dstAngle) = (dstAngle, srcAngle);
_src = _center + new Vector2((float)Math.Cos(srcAngle), (float)Math.Sin(srcAngle)) * _radius;
_dst = _center + new Vector2((float)Math.Cos(dstAngle), (float)Math.Sin(dstAngle)) * _radius;
_beforeStartSrc = CommonMath.Transform2D(_src,
(_direction >= 0 ? _angleStart : _angleEnd) + 90 * _direction, new Vector2(-1000, 0));
_afterEndDst = CommonMath.Transform2D(_dst, (_direction >= 0 ? _angleEnd : _angleStart) + 90 * _direction,
new Vector2(1000, 0));
_tangentSrc = QueryTangentPoint(_src).Angle;
_tangentDst = QueryTangentPoint(_dst).Angle;
}
private Vector2 _center;
private float _radius, _angleStart, _angleEnd;
private int _direction;
private float _totalTh, _totalLen;
private Vector2 _src, _dst;
private float _tangentSrc, _tangentDst;
private Vector2 _beforeStartSrc, _afterEndDst;
private Vector2[] _visPoints = Array.Empty();
}
}