Files
ParkingRobot/CommonUsage-MultiVehicleSync/commonusage/Geometries/CircularArc.cs
T

257 lines
6.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
{
/// <summary>
/// 以center为圆心、radius为半径,从angleStart逆时针转到angleEnd所构成的圆弧。direction表示圆弧走向。
/// </summary>
/// <param name="center"></param>
/// <param name="radius"></param>
/// <param name="angleStart"></param>
/// <param name="angleEnd"></param>
/// <param name="direction">表示圆弧走向,1为angleStart到angleEnd-1为angleEnd到angleStart</param>
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<VisDot> processDot, Action<VisLine> 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<Vector2>();
}
}