直接上代码:
首先是线段类和圆类的定义:
using UnityEngine;
public class Circle
{
Vector2 c;
float r;
public Vector2 center { get { return c; } }
public float radius { get { return r; } }
public Circle(Vector2 center, float radius)
{
SetCenter(center);
SetRadius(radius);
}
public void SetCenter(Vector2 center) { c = center; }
public void SetRadius(float radius) { r = radius; }
}
public class Segment2D
{
Vector2 _pointA = Vector2.zero;
public Vector2 pointA
{
get { return _pointA; }
set
{
if (_pointA != value)
{
_pointA = value;
Update();
}
}
}
Vector2 _pointB = Vector2.zero;
public Vector2 pointB
{
get { return _pointB; }
set
{
if (_pointB != value)
{
_pointB = value;
Update();
}
}
}
float _length = 0;
public float length { get { return _length; } }
Vector2 _normal = Vector2.zero;
public Vector2 normal { get { return _normal; } }
public Segment2D(Vector2 pointA, Vector2 pointB)
{
SetPoints(pointA, pointB);
}
public void SetPoints(Vector2 pointA, Vector2 pointB)
{
bool updateEnabled = false;
if (_pointA != pointA)
{
_pointA = pointA;
updateEnabled = true;
}
if (_pointB != pointB)
{
_pointB = pointB;
updateEnabled = true;
}
if (updateEnabled) Update();
}
void Update()
{
_length = Vector2.Distance(_pointA, _pointB);
_normal = _length > 0 ? (_pointB - pointA) / _length : Vector2.zero;
}
}
然后是计算类:
public class Calculate
{
//计算直线和圆的交点
static public Vector2[] GetLineCircleIntersections(Segment2D seg, Circle circle)
{
float cR = circle.radius;
if (Mathf.Approximately(seg.length, 0)) return new Vector2[0];
if (Mathf.Approximately(cR, 0)) return new Vector2[0];
float pAx = seg.pointA.x;
float pAy = seg.pointA.y;
float pBx = seg.pointB.x;
float pBy = seg.pointB.y;
float ccx = circle.center.x;
float ccy = circle.center.y;
float discriminant;
bool isSegSlopeLegal = !Mathf.Approximately(pAx, pBx);
if (!isSegSlopeLegal)
{
discriminant = cR * cR - (pAx - ccx) * (pAx - ccx);
if (Mathf.Approximately(discriminant, 0))
{
return new Vector2[] { new Vector2(pAx, ccy) };
}
else
{
if (discriminant < 0) return new Vector2[0];
//
float sqrtDisc = Mathf.Sqrt(discriminant);
float yA = ccy + sqrtDisc;
float yB = ccy - sqrtDisc;
return new Vector2[] { new Vector2(pAx, yA), new Vector2(pAx, yB) };
}
}
float lineA = (pAy - pBy) / (pAx - pBx);
float lineB = (pAy * pBx - pBy * pAx) / (pBx - pAx);
float a = lineA * lineA + 1;
float b = 2 * lineA * (lineB - ccy) - 2 * ccx;
float c = ccx * ccx + (lineB - ccy) * (lineB - ccy) - cR * cR;
discriminant = b * b - 4 * a * c;
if (!Mathf.Approximately(discriminant, 0))
{
if (discriminant < 0) return new Vector2[0];
//
float sqrtDisc = Mathf.Sqrt(discriminant);
float xA = (-b + sqrtDisc) / (2 * a);
float yA = lineA * xA + lineB;
float xB = (-b - sqrtDisc) / (2 * a);
float yB = lineA * xB + lineB;
return new Vector2[] { new Vector2(xA, yA), new Vector2(xB, yB) };
}
else
{
float x = -b / (2 * a);
float y = lineA * x + lineB;
return new Vector2[] { new Vector2(x, y) };
}
}
//计算线段和圆的交点
static public Vector2[] GetSegmentCircleIntersections(Segment2D seg, Circle circle)
{
float cR = circle.radius;
if (Mathf.Approximately(seg.length, 0)) return new Vector2[0];
if (Mathf.Approximately(cR, 0)) return new Vector2[0];
float sqrtRadius = cR * cR;
bool aInC = (seg.pointA - circle.center).sqrMagnitude < sqrtRadius;
bool bInC = (seg.pointB - circle.center).sqrMagnitude < sqrtRadius;
if (aInC && bInC) return new Vector2[0];
float pAx = seg.pointA.x;
float pAy = seg.pointA.y;
float pBx = seg.pointB.x;
float pBy = seg.pointB.y;
float ccx = circle.center.x;
float ccy = circle.center.y;
float discriminant;
bool isSegSlopeLegal = !Mathf.Approximately(pAx, pBx);
if (!isSegSlopeLegal)
{
discriminant = cR * cR - (pAx - ccx) * (pAx - ccx);
if (Mathf.Approximately(discriminant, 0))
{
if ((pAy - ccy) * (pBy - ccy) > 0) return new Vector2[0];
return new Vector2[] { new Vector2(pAx, ccy) };
}
if (discriminant < 0) return new Vector2[0];
//
float sqrtDisc = Mathf.Sqrt(discriminant);
float yA = ccy + sqrtDisc;
float yB = ccy - sqrtDisc;
if (aInC) return new Vector2[] { new Vector2(pAx, yB) };
if (bInC) return new Vector2[] { new Vector2(pAx, yA) };
return new Vector2[] { new Vector2(pAx, yA), new Vector2(pAx, yB) };
}
float lineA = (pAy - pBy) / (pAx - pBx);
float lineB = (pAy * pBx - pBy * pAx) / (pBx - pAx);
float a = lineA * lineA + 1;
float b = 2 * lineA * (lineB - ccy) - 2 * ccx;
float c = ccx * ccx + (lineB - ccy) * (lineB - ccx) - cR * cR;
discriminant = b * b - 4 * a * c;
if (!Mathf.Approximately(discriminant, 0))
{
if (discriminant < 0) return new Vector2[0];
//
float sqrtDisc = Mathf.Sqrt(discriminant);
float xA = (-b + sqrtDisc) / (2 * a);
float yA = lineA * xA + lineB;
float xB = (-b - sqrtDisc) / (2 * a);
float yB = lineA * xB + lineB;
if (aInC) return new Vector2[] { new Vector2(xA, yA) };
if (bInC) return new Vector2[] { new Vector2(xB, yB) };
return new Vector2[] { new Vector2(xA, yA), new Vector2(xB, yB) };
}
else
{
float x = -b / (2 * a);
float y = lineA * x + lineB;
Vector2 point = new Vector2(x, y);
if (Vector2.Dot(seg.pointA - point, seg.pointB - point) > 0) return new Vector2[0];
return new Vector2[] { point };
}
}
}