Unity 面试题汇总(六)常用的一些几何算法
目录
1、怎么判断一个点是否在直线上
已知点P(x,y),以及直线上的两点A(x1,y1)、B(x2,y2),可以通过计算向量AP与向量AB的叉乘是否等于0来计算点P是否在直线AB上
知识点:叉乘
/// <summary>
/// 2D叉乘
/// </summary>
/// <param name="v1">点1</param>
/// <param name="v2">点2</param>
/// <returns></returns>
public static float CrossProduct2D(Vector2 v1,Vector2 v2)
{
//叉乘运算公式 x1*y2 - x2*y1
return v1.x * v2.y - v2.x * v1.y;
}
/// <summary>
/// 点是否在直线上
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static bool IsPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
float value = CrossProduct2D(point - lineStart, lineEnd - lineStart);
return Mathf.Abs(value) <0.0003 /* 使用 Mathf.Approximately(value,0) 方式,在斜线上好像无法趋近为0*/;
}
2、判断点是否在线段上
已知点P(x,y),以及线段A(x1,y1),B(x2,y2)。
1)方法一
可以进行下面两部来判断点P是否在线段AB上:
(1)点是否在线段AB所在的直线上(点是否在直线上
)
(2)点是否在以线段AB为对角线的矩形上,来忽略点在线段AB延长线上
/// <summary>
/// 2D叉乘
/// </summary>
/// <param name="v1">点1</param>
/// <param name="v2">点2</param>
/// <returns></returns>
public static float CrossProduct2D(Vector2 v1,Vector2 v2)
{
//叉乘运算公式 x1*y2 - x2*y1
return v1.x * v2.y - v2.x * v1.y;
}
/// <summary>
/// 点是否在直线上
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static bool IsPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
float value = CrossProduct2D(point - lineStart, lineEnd - lineStart);
return Mathf.Abs(value) <0.0003 /* 使用 Mathf.Approximately(value,0) 方式,在斜线上好像无法趋近为0*/;
}
/// <summary>
/// 点是否在线段上
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static bool IsPointOnSegment(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
//1.先通过向量的叉乘确定点是否在直线上
//2.在拍段点是否在指定线段的矩形范围内
if (IsPointOnLine(point,lineStart,lineEnd))
{
//点的x值大于最小,小于最大x值 以及y值大于最小,小于最大
if (point.x >= Mathf.Min(lineStart.x, lineEnd.x) && point.x <= Mathf.Max(lineStart.x, lineEnd.x) &&
point.y >= Mathf.Min(lineStart.y, lineEnd.y) && point.y <= Mathf.Max(lineStart.y, lineEnd.y))
return true;
}
return false;
}
2)方法二
计算向量AP
的长度加上向量BP
的长度是否等于向量AB
的长度,来确定点P是否在线段AB上
/// <summary>
/// 点是否在线段上
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static bool IsPointOnSegment2(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
return Mathf.Approximately(Mathf.Abs((lineStart - point).magnitude) + Mathf.Abs((lineEnd - point).magnitude),
Mathf.Abs((lineEnd - lineStart).magnitude));
}
3、判断点与线的位置关系
已知点P(x,y),与直线上A(x1,y1),B(x2,y2)两点,通过向量AP与BP的叉乘返回的结果,即可确定点在直线的位置关系。
判断依据:1)等于0:点在直线上;2)小于0:点在直线的左侧;3)大于0:点在直线的右侧
/// <summary>
/// 2D叉乘
/// </summary>
/// <param name="v1">点1</param>
/// <param name="v2">点2</param>
/// <returns></returns>
public static float CrossProduct2D(Vector2 v1,Vector2 v2)
{
//叉乘运算公式 x1*y2 - x2*y1
return v1.x * v2.y - v2.x * v1.y;
}
/// <summary>
/// 点与线的位置关系
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns>==0:点在线上 <0:点在线的左侧 >0:点在线的右侧</returns>
public static int IsPointToLinePosition(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
float crossValue = CrossProduct2D(point - lineStart, lineEnd - lineStart);
if (crossValue < 0) return -1;
if (crossValue > 0) return 1;
return 0;
}
4、计算点在直线上的投影(向量投影)
已知点P(x,y),与直线上两点A(x1,y1),B(x2,2),求P点在直线AB上的投影
知识点:通过向量投影公式(点乘),即可获得点P在直线AB投影点。
/// <summary>
/// 2D叉乘
/// </summary>
/// <param name="v1">点1</param>
/// <param name="v2">点2</param>
/// <returns></returns>
public static float CrossProduct2D(Vector2 v1,Vector2 v2)
{
//叉乘运算公式 x1*y2 - x2*y1
return v1.x * v2.y - v2.x * v1.y;
}
/// <summary>
/// 点是否在直线上
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static bool IsPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
float value = CrossProduct2D(point - lineStart, lineEnd - lineStart);
return Mathf.Abs(value) <0.0003 /* 使用 Mathf.Approximately(value,0) 方式,在斜线上好像无法趋近为0*/;
}
/// <summary>
/// 点到直线上的投影坐标
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static Vector2 Point2LineProject(Vector2 point,Vector2 lineStart,Vector2 lineEnd)
{
if (IsPointOnLine(point,lineStart,lineEnd))
return point;
Vector2 v = point - lineStart;
Vector2 u = lineEnd - lineStart;
//求出u'的长度
float u1Length = Vector2.Dot(u, v) / u.magnitude;
return u1Length * u.normalized + lineStart;
}
5、计算点到直线距离
已点P(x,y),与直线上两点A(x1,y1),B(x2,y2),求点P到直线AB的距离
1)方法1:
先求出点P在直线AB上的投影点P’,求出点P与点P’的距离即可。
/// <summary>
/// 2D叉乘
/// </summary>
/// <param name="v1">点1</param>
/// <param name="v2">点2</param>
/// <returns></returns>
public static float CrossProduct2D(Vector2 v1,Vector2 v2)
{
//叉乘运算公式 x1*y2 - x2*y1
return v1.x * v2.y - v2.x * v1.y;
}
/// <summary>
/// 点是否在直线上
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static bool IsPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
float value = CrossProduct2D(point - lineStart, lineEnd - lineStart);
return Mathf.Abs(value) <0.0003 /* 使用 Mathf.Approximately(value,0) 方式,在斜线上好像无法趋近为0*/;
}
/// <summary>
/// 点到直线上的投影坐标
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static Vector2 Point2LineProject(Vector2 point,Vector2 lineStart,Vector2 lineEnd)
{
if (IsPointOnLine(point,lineStart,lineEnd))
return point;
Vector2 v = point - lineStart;
Vector2 u = lineEnd - lineStart;
//求出u'的长度
float u1Length = Vector2.Dot(u, v) / u.magnitude;
return u1Length * u.normalized + lineStart;
}
/// <summary>
/// 点到直线的距离
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static float Point2LineDistance(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
return Vector2.Distance(point, Point2LineProject(point, lineStart, lineEnd));
}
2)方法2:
通过海伦公式+底x高三角形面积公式,就可求得点P到直线AB的距离。
海伦公式:知道三角形3条边长,求三角形面积。链接:海伦公式
底x高三角形面积公式:底x高=2倍的三角形面积
/// <summary>
/// 2D叉乘
/// </summary>
/// <param name="v1">点1</param>
/// <param name="v2">点2</param>
/// <returns></returns>
public static float CrossProduct2D(Vector2 v1,Vector2 v2)
{
//叉乘运算公式 x1*y2 - x2*y1
return v1.x * v2.y - v2.x * v1.y;
}
/// <summary>
/// 点是否在直线上
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static bool IsPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
float value = CrossProduct2D(point - lineStart, lineEnd - lineStart);
return Mathf.Abs(value) <0.0003 /* 使用 Mathf.Approximately(value,0) 方式,在斜线上好像无法趋近为0*/;
}
/// <summary>
/// 点到直线的距离
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static float Point2LineDistance(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
//先判断点是否在线上
if (IsPointOnLine(point, lineStart, lineEnd)) return 0;
//通过海伦公式求得三角形面积,然后面积除以底,得到高度
//point 简写为p lineStart简写为s lineEnd简写为e
float se = Vector2.Distance(lineStart, lineEnd);
float sp = Vector2.Distance(lineStart, point);
float ep = Vector2.Distance(lineEnd, point);
//海伦公式 + 底x高面积公式
//半周长
float p = (se + sp + ep) / 2;
//求面积
float s = Mathf.Sqrt(p * (p - se) * (p - sp) * (p - ep));
//面积除以底得高度
return 2 * s / se;
}
6、计算点到线段的距离
已知点P(x,y),与线段A(x1,y1),B(x2,y2),计算点到线段的距离
思路:先求出点到直线AB(包括线段AB的延长线)的投影点,在判断点是否在线段AB的矩形区间内,在计算两点的距离即可。
/// <summary>
/// 2D叉乘
/// </summary>
/// <param name="v1">点1</param>
/// <param name="v2">点2</param>
/// <returns></returns>
public static float CrossProduct2D(Vector2 v1,Vector2 v2)
{
//叉乘运算公式 x1*y2 - x2*y1
return v1.x * v2.y - v2.x * v1.y;
}
/// <summary>
/// 点是否在直线上
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static bool IsPointOnLine(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
float value = CrossProduct2D(point - lineStart, lineEnd - lineStart);
return Mathf.Abs(value) <0.0003 /* 使用 Mathf.Approximately(value,0) 方式,在斜线上好像无法趋近为0*/;
}
/// <summary>
/// 点到直线上的投影坐标
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static Vector2 Point2LineProject(Vector2 point,Vector2 lineStart,Vector2 lineEnd)
{
if (IsPointOnLine(point,lineStart,lineEnd))
return point;
Vector2 v = point - lineStart;
Vector2 u = lineEnd - lineStart;
//求出u'的长度
float u1Length = Vector2.Dot(u, v) / u.magnitude;
return u1Length * u.normalized + lineStart;
}
/// <summary>
/// 点到线段的距离
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns></returns>
public static float Point2SegmentDistance(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
Vector2 projectPoint = Point2LineProject(point,lineStart,lineEnd);
if (projectPoint.x >= Mathf.Min(lineStart.x, lineEnd.x) &&
projectPoint.x <= Mathf.Max(lineStart.x, lineEnd.x) &&
projectPoint.y >= Mathf.Min(lineStart.y, lineEnd.y) && projectPoint.y <= Mathf.Max(lineStart.y, lineEnd.y))
return Vector2.Distance(point, projectPoint);
return float.MaxValue;
}
7、判断多边形是否为凸多边形
凸(凹)多边形 计算方法
已知多边形的逆时针顶点序列,依次判断相邻两个线段走向是否一致即可。
(点与线段位置关系,因为规定为逆时针顶点序列,所以只要两个相邻线段的叉乘都小于0则该多边形为凸多边形)
/// <summary>
/// 2D叉乘
/// </summary>
/// <param name="v1">点1</param>
/// <param name="v2">点2</param>
/// <returns></returns>
public static float CrossProduct2D(Vector2 v1,Vector2 v2)
{
//叉乘运算公式 x1*y2 - x2*y1
return v1.x * v2.y - v2.x * v1.y;
}
/// <summary>
/// 点与线的位置关系
/// </summary>
/// <param name="point"></param>
/// <param name="lineStart"></param>
/// <param name="lineEnd"></param>
/// <returns>==0:点在线上 <0:点在线的左侧 >0:点在线的右侧</returns>
public static int IsPointToLinePosition(Vector2 point, Vector2 lineStart, Vector2 lineEnd)
{
float crossValue = CrossProduct2D(point - lineStart, lineEnd - lineStart);
if (crossValue < 0) return -1;
if (crossValue > 0) return 1;
return 0;
}
/// <summary>
/// 是否为凸多边形
/// </summary>
/// <param name="points">逆时针点序列</param>
/// <returns></returns>
public static bool IsConvexPolygon(List<Vector2> points)
{
//计算每个顶点的转向,如果有不一致的转向,则表示该多边形不是凸多边形
if (points.Count < 3) return false;
bool isConvex = true;
for (int i = 1; i < points.Count; i++)
{
Vector2 point = points[i];
//上一个点
Vector2 point1 = points[i - 1];
//下一个点,如果超出当前点集合,则需要获取第一个点
int nextIndex = i + 1;
if (nextIndex >= points.Count) nextIndex = 0;
Vector2 point2 = points[nextIndex];
//计算朝向,因为点集合为逆时针点序列,如果点在线段右侧,则表示该角大于180 该多边形为凹多边形
float value = IsPointToLinePosition(point1, point,point2);
if (value > 0)
{
isConvex = false;
break;
}
}
return isConvex;
}
8、判断线段与线段是否共线
思路:
1)先判断两条线段是否平行,即两条线段的叉积等于0
2)在判断两条线段是否共线,即线段1一个点在线段2的延长线上
/// <summary>
/// 线段与线段是否共线
/// </summary>
/// <param name="segment1Start"></param>
/// <param name="segment1End"></param>
/// <param name="segment2Start"></param>
/// <param name="segment2End"></param>
/// <returns></returns>
public static bool IsSegmentCollineation(Vector2 segment1Start, Vector2 segment1End, Vector2 segment2Start,
Vector2 segment2End)
{
//1.判断两个向量是否平行
float value = CrossProduct2D(segment1End - segment1Start, segment2End - segment2Start);
if (Mathf.Abs(value)<0.0003f)
{
// 平行,则判断一个线上的点是否在另一线上
if (IsPointOnLine(segment2Start, segment2End , segment2Start))
return true;
}
return false;
}
9、判断线段与线段是否重合(非相交)
1)判断两条线段必须共线
2)然后片段判断线段A的起终点是否在线段B,以及线段B的起终点,是否在线段A上,只要有一个条件成立,则可以认为两条线段是重合的
或
1)判断两条线段是否共线
2)对两条线段的4个定点进行排序,如果1,3 或 1,4 为为同一条线段上的点,则可以任务两条线段是否重合的
/// <summary>
/// 线段与线段是否重合(全部重合或局部重合)
/// </summary>
/// <param name="segment1Start"></param>
/// <param name="segment1End"></param>
/// <param name="segment2Start"></param>
/// <param name="segment2End"></param>
/// <returns></returns>
public static bool IsSegmentCoincide(Vector2 segment1Start, Vector2 segment1End, Vector2 segment2Start,
Vector2 segment2End)
{
//先判断两条线段是否在同一条线上
if (!IsSegmentCollineation(segment1Start,segment1End,segment2Start,segment2End))
return false;
//如果是相同的起终点
if (segment1Start == segment2Start && segment1End == segment2End) return true;
//判断检测点是否在另一条线段上
if (IsPointOnSegment2(segment1Start, segment2Start, segment2End) ||
IsPointOnSegment2(segment1End, segment2Start, segment2End) ||
IsPointOnSegment2(segment2Start, segment1Start, segment1End) ||
IsPointOnSegment2(segment2End, segment1Start, segment1End))
return true;
return false;
}
10、线段与线段是否相交
/// <summary>
/// 线段是否相交
/// </summary>
/// <param name="segment1Start"></param>
/// <param name="segment1End"></param>
/// <param name="segment2Start"></param>
/// <param name="segment2End"></param>
/// <returns></returns>
public static bool IsSegmentIntersect(Vector2 segment1Start, Vector2 segment1End, Vector2 segment2Start,
Vector2 segment2End)
{
//快速排斥实验
if (Mathf.Min(segment1Start.x,segment1End.x) <= Mathf.Max(segment2Start.x,segment2End.x)
&& Mathf.Min(segment2Start.x,segment2End.x) <= Mathf.Max(segment2Start.x,segment2End.x)
&& Mathf.Min(segment1Start.y,segment1End.y)<=Mathf.Max(segment2Start.y,segment2End.y)
&& Mathf.Min(segment2Start.y,segment2End.y) <= Mathf.Max(segment1Start.y,segment1End.y))
{
//先判断线段是否重合,重合,则认为也是相交
if (IsSegmentCoincide(segment1Start, segment1End, segment2Start, segment2End))
return true;
//互为跨立的判断 一线的点相对另一线的位置关系,左右 -1 1,之和为 0
int state = IsPointToLinePosition(segment1Start, segment2Start, segment2End) +
IsPointToLinePosition(segment1End, segment2Start, segment2End) +
IsPointToLinePosition(segment2Start, segment1Start, segment1End) +
IsPointToLinePosition(segment2End, segment1Start, segment1End);
if (state==0) return true;
}
return false;
}
11、计算直线与直线的交点
/// <summary>
/// 求直线的交点
/// </summary>
/// <param name="line1Start"></param>
/// <param name="line2End"></param>
/// <param name="line2Start"></param>
/// <param name="line2End"></param>
/// <returns></returns>
public static Vector2 LineIntersectPoint(Vector2 line1Start, Vector2 line1End, Vector2 line2Start,
Vector2 line2End)
{
//两点式公式
//x0 = ((x3-x4) * (x2*y1 - x1*y2) - (x1-x2) * (x4*y3 - x3*y4)) / ((x3-x4) * (y1-y2) - (x1-x2) * (y3-y4));
//y0 = ((y3-y4) * (y2*x1 - y1*x2) - (y1-y2) * (y4*x3 - y3*x4)) / ((y3-y4) * (x1-x2) - (y1-y2) * (x3-x4));
float x1 = line1Start.x, x2 = line1End.x, x3 = line2Start.x, x4 = line2End.x;
float y1 = line1Start.y, y2 = line1End.y, y3 = line2Start.y, y4 = line2End.y;
Vector2 point = Vector2.zero;
point.x = ((x3-x4) * (x2*y1 - x1*y2) - (x1-x2) * (x4*y3 - x3*y4)) / ((x3-x4) * (y1-y2) - (x1-x2) * (y3-y4));
point.y = ((y3-y4) * (y2*x1 - y1*x2) - (y1-y2) * (y4*x3 - y3*x4)) / ((y3-y4) * (x1-x2) - (y1-y2) * (x3-x4));
return point;
}
12、线段与线段的交点
1)先判断线段与线段是否相交。
2)如果相交,则获取直线与直线的交点
其中,需要注意线段平行与重合情况。
平行:无交点
全部重合:返回其中一条线段的起点与终点
部分重合:返回重合部分的起点与终点
/// <summary>
/// 线段与线段的交点
/// </summary>
/// <param name="segment1Start"></param>
/// <param name="segment1End"></param>
/// <param name="segment2Start"></param>
/// <param name="segment2End"></param>
/// <param name="points"></param>
/// <returns></returns>
public static bool SegmentIntersectPoint(Vector2 segment1Start,Vector2 segment1End,Vector2 segment2Start,Vector2 segment2End,out List<Vector2> points)
{
//线段相同情况下 直接返回其中一条线段即可
if (segment1Start==segment2Start && segment1End== segment2End)
{
points = new List<Vector2>(){segment1Start,segment1End};
return true;
}
points = new List<Vector2>();
//线段平行,且不共线,则无交点
float value = CrossProduct2D(segment1End - segment1Start, segment2End - segment2Start);
if (Mathf.Approximately(Mathf.Abs(value), 0))
{
//共线判断,一点是否在另一条线段上
if (!IsPointOnLine(segment1Start,segment2Start,segment2End))
return false;
//平行且共线的情况下,需要知道线段是否重合
if (IsPointOnSegment2(segment1Start, segment2Start, segment2End))
points.Add(segment1Start);
if (IsPointOnSegment2(segment1End, segment2Start, segment2End))
points.Add(segment1End);
if (IsPointOnSegment2(segment2Start, segment1Start, segment1End))
points.Add(segment2Start);
if (IsPointOnSegment2(segment2End, segment1Start, segment1End))
points.Add(segment2End);
if (points.Count <= 0) return false;
return true;
}
//不平行,且不共线,则需要判断两个线段是否相交
if (!IsSegmentIntersect(segment1Start, segment1End, segment2Start, segment2End))
return false;
//计算直线与直线的交点
Vector2 outPoint = Vector2.zero;
if (LineIntersectPoint(segment1Start,segment1End,segment2Start,segment2End,out outPoint))
{
points.Add(outPoint);
return true;
}
/*********用此种方式也可*************************/
//1.先求交点
//2.在计算点是否在两线段上
/*
if (LineIntersectPoint(segment1Start,segment1End,segment2Start,segment2End,out outPoint))
{
if (IsPointOnLine(outPoint,segment1Start,segment1End) && IsPointOnLine(outPoint,segment2Start,segment2End))
{
points.Add(outPoint);
return true;
}
}*/
return false;
}
13、射线与射线是否相交,以及交点
1)判断射线所在的直线是否相交
2)判断点是否在两条射线上
/// <summary>
/// 射线是否相交
/// </summary>
/// <param name="ray1Start"></param>
/// <param name="ray1Dir"></param>
/// <param name="ray2Start"></param>
/// <param name="ray2Dir"></param>
/// <returns></returns>
public static bool IsRayIntersect(Vector2 ray1Start, Vector2 ray1Dir, Vector2 ray2Start, Vector2 ray2Dir,out Vector2 point)
{
//先计算两条直线是否相交
if (!LineIntersectPoint(ray1Start,ray1Start+ray1Dir * 1,ray2Start,ray2Start+ray2Dir*1,out point))
return false;
if (Vector2.Dot((point - ray1Start).normalized, ray1Dir.normalized) < 0) return false;
if (Vector2.Dot((point - ray2Start).normalized, ray2Dir.normalized) < 0) return false;
return true;
}
14、射线与线段是否相交,以及交点
1)所在直线是否相交
2)点是否在射线以及直线上
/// <summary>
/// 射线与线段是否相交
/// </summary>
/// <param name="rayStart"></param>
/// <param name="rayDir"></param>
/// <param name="segmentStart"></param>
/// <param name="segmentEnd"></param>
/// <returns></returns>
public static bool IsRaySegmentIntersect(Vector2 rayStart,Vector2 rayDir,Vector2 segmentStart,Vector2 segmentEnd,out Vector2 point)
{
//先计算两条直线是否相交
if (!LineIntersectPoint(rayStart,rayStart+rayDir * 1,segmentStart,segmentEnd,out point))
return false;
//判断交点的位置是否在射线上 方向相同可以确定点在射线上
if (Vector2.Dot((point - rayStart).normalized, rayDir.normalized) < 0) return false;
//点是否在线段上
if (!IsPointOnSegment2(point,segmentStart, segmentEnd)) return false;
return true;
}
15.点围绕另一点旋转指定角度
需要注意的是,需要将角度转为弧度制进行计算(角度与弧度)
/// <summary>
/// 点绕另一个点进行旋转
/// </summary>
/// <param name="originPoint"></param>
/// <param name="point"></param>
/// <param name="angle"></param>
/// <returns></returns>
public static Vector2 PointRoationOnPoint(Vector2 originPoint, Vector2 point, float angle)
{
if (originPoint == point) return originPoint;
Vector2 resultPoint = Vector2.zero;
Vector2 v= (point - originPoint).normalized;
angle *= Mathf.Deg2Rad;
resultPoint.x = v.x * Mathf.Cos(angle) - v.y * Mathf.Sin(angle);
resultPoint.y = v.x * Mathf.Sin(angle) + v.y * Mathf.Cos(angle);
return resultPoint * Vector2.Distance(originPoint,point) + originPoint ;
}
/// <summary>
/// 点集合绕点旋转
/// </summary>
/// <param name="points"></param>
/// <param name="point"></param>
/// <param name="angle"></param>
/// <returns></returns>
public static List<Vector2> PointsRoationOnPoint(List<Vector2> points, Vector2 point, float angle)
{
List<Vector2> resultPoints = new List<Vector2>();
if (points == null) return resultPoints;
for (int i = 0; i < points.Count; i++)
{
Vector2 nPoint = PointRoationOnPoint(point,points[i],angle);
resultPoints.Add(nPoint);
}
return resultPoints;
}
16、点是否在任意多变内
判断点是否在任意多变内部的几种方法
射线法:
1)求出多边形所在矩形,判断点是否在矩形内
2)点是否在多边形顶点或边上
3)以当前点做一条水平射线,计算该射线与多边形线段的相交数量,如果为奇数则点在多边形内部
/// <summary>
/// 点是否在任意多边形内部
/// </summary>
/// <param name="point"></param>
/// <param name="polygon"></param>
/// <returns>-1:不在多边形内 0:在多边形上 1:多边形内 </returns>
public static int IsPointInAnyPolygon(Vector2 point, List<Vector2> polygon)
{
//顶点数量小于3,则无法形成多边形
if (polygon.Count < 3) return -1;
//1.先获取多边形所在矩形范围内
Vector2 rectMin = polygon[0], rectMax = polygon[0];
for (int i = 1; i < polygon.Count; i++)
{
if (polygon[i].x < rectMin.x) rectMin.x = polygon[i].x;
if (polygon[i].y < rectMin.y) rectMin.y = polygon[i].y;
if (polygon[i].x > rectMax.x) rectMax.x = polygon[i].x;
if (polygon[i].y > rectMax.y) rectMax.y = polygon[i].y;
}
if (point.x < rectMin.x || point.y < rectMin.y || point.x > rectMax.x || point.y > rectMax.y) return -1;
//2.射线相交点计算
int intersectCount = 0;
for (int i = 0; i < polygon.Count; i++)
{
int nextIndex = i + 1;
if (nextIndex >= polygon.Count)
nextIndex = 0;
//目标在顶点上
if (polygon[i] == point || polygon[nextIndex] == point)
return 0;
//目标在线段上
if (IsPointOnSegment2(point, polygon[i], polygon[nextIndex]))
return 0;
Vector2 intersectPoint;
//射线与线段相交
if (IsRaySegmentIntersect(point,Vector2.right,polygon[i],polygon[nextIndex],out intersectPoint))
{
//如果相交为线段的顶点,则需要增加2,因为在同一点进入,又在同一个点出去
if (intersectPoint == polygon[i] || intersectPoint == polygon[nextIndex])
intersectCount += 2;
else
intersectCount += 1;
}
}
return intersectCount % 2 == 1 ? 1:-1;
}
17、点是否在椭圆内
如果P(x,y)是否在椭圆内,则小于等于1
焦点在x轴上时,长轴为2a,短轴为2b,圆方程为x^2/a^2+y^2/b^2=1
焦点在y轴上时,长轴为2a,短轴为2b,圆方程为x^2/b^2+y^2/a^2=1
笔误
/// <summary>
/// 点是否在椭圆内
/// </summary>
/// <param name="point">目标点</param>
/// <param name="xAxis">长轴半径</param>
/// <param name="yAxis">短轴半径</param>
/// <returns></returns>
private bool PointIsInEllipse(Vector2 point,float xAxis,float yAxis)
{
return (point.x * point.x) / (xAxis * xAxis ) + (point.y * point.y) / (yAxis * yAxis )<=1;
}
18、直线与椭圆的交点计算
/// <summary>
/// 线段和椭圆弧的交点
/// </summary>
/// <param name="line">线段对象</param>
/// <param name="ht">存储交点和error信息</param>
/// <returns>返回交点和error信息</returns>
internal static Hashtable LineIntersectEllipticArc(Line line, Hashtable ht)
{
//线段的终点
Vector2 LineendPoint = line.endPoint;
//线段的起点
Vector2 LinestartPoint = line.startPoint;
//椭圆弧的长轴的一半
double maxAxis = 262.39051820125013 / 2;
//椭圆弧的短轴一半
double minAxis = 135.76528231694766 / 2;
//椭圆弧的起点
//椭圆弧的起始角度
double startAngle = DegreeToRadian(132.0438345714015);
//椭圆弧的终点
//椭圆弧的终止角度 //默认设置为90度对应的弧度
double endAngle = DegreeToRadian(258.13766538237763);
//椭圆弧的圆心
Vector2 center = new Vector2(17.7894639270817, 15.0254309579905);
//设置原点坐标(0,0)
Vector2 centerZero = new Vector2(0, 0);
//椭圆弧的导入角度'
double angle = DegreeToRadian(191.75357833501226);
//将直线顺时针旋转angle度
Vector2[] ve = Anticlockwise(LineendPoint, LinestartPoint, center, angle);
//假设默认交点坐标
Vector2 ptInter1 = new Vector2(0, 0);
Vector2 ptInter2 = new Vector2(0, 0);
//直线和椭圆的交点坐标
LineIntersectEllipse(MoveOne(ve[0], center), MoveOne(ve[1], center), maxAxis, minAxis, ref ptInter1, ref ptInter2);
//用于存储交点
List<Vector2> node = new List<Vector2>();
//用于判断是否有交点
bool judgeNode = false;
//存储的版本号
int version = 0;
double a = NormalizeRadianAngle(startAngle);
double b = NormalizeRadianAngle(endAngle);
if (Zero(ptInter1))
{
JudgeDotQualified(centerZero, ptInter1, a, b, node);
if (node.Count == 1)
{
if (node[0] != null)
{
//表示第一次有一个值
//将交点逆时针旋转
Vector2[] ve1 = Anticlockwise(ptInter1, ptInter2, centerZero, angle, false);
//再将交点平移得到最后真正的交点
ve1[0] = MoveTwo(ve1[0], center);
node[0] = ve1[0];
version = 1;
}
}
}
if (Zero(ptInter2))
{
if (version == 0)
{
JudgeDotQualified(centerZero, ptInter2, a, b, node);
if (node.Count == 1)
{
if (node[0] != null)
{
//表示第一次没有一个值
//将交点逆时针旋转
Vector2[] ve1 = Anticlockwise(ptInter1, ptInter2, centerZero, angle, false);
//再将交点平移得到最后真正的交点
ve1[1] = MoveTwo(ve1[1], center);
node[0] = ve1[1];
}
}
}
else if (version == 1)
{
JudgeDotQualified(centerZero, ptInter2, a, b, node);
if (node.Count == 2)
{
if (node[1] != null)
{
//表示第一次有一个值
//将交点逆时针旋转
Vector2[] ve1 = Anticlockwise(ptInter1, ptInter2, centerZero, angle, false);
//再将交点平移得到最后真正的交点
ve1[1] = MoveTwo(ve1[1], center);
node[1] = ve1[1];
}
}
}
}
ht.Add("node", node);
return ht;
}
/// <summary>
/// 角度转弧度
/// </summary>
/// <param name="angle">角度</param>
/// <returns>弧度</returns>
public static double DegreeToRadian(double angle)
{
return ((angle * System.Math.PI) / 180.0);
}
public const double EPSILON = 1E-12;
public static bool IsEqual(double x, double y, double epsilon = EPSILON)
{
return IsEqualZero(x - y, epsilon);
}
public static bool IsEqualZero(double x, double epsilon = EPSILON)
{
return (System.Math.Abs(x) < epsilon);
}
/// <summary>
/// 点的绕椭圆弧弧心旋转
/// </summary>
/// <param name="LineendPoint">线段终点</param>
/// <param name="LinestartPoint">线段起点</param>
/// <param name="center">椭圆弧弧心</param>
/// <param name="angle">椭圆弧的导入角度'</param>
/// /// <param name="isClockwise">顺逆旋转,默认为顺时针旋转</param>
internal static Vector2[] Anticlockwise(Vector2 LineendPoint, Vector2 LinestartPoint, Vector2 center, double angle, bool isClockwise = true)
{
Vector2[] ve = new Vector2[2];
if (isClockwise)
{
angle = -angle;
}
double cos = System.Math.Cos(angle);
double sin = System.Math.Sin(angle);
if (IsEqual(cos, 0))
{
cos = 0;
}
if (IsEqual(sin, 0))
{
sin = 0;
}
double x = ((LineendPoint.x - center.x) * cos - (LineendPoint.y - center.y) * sin + center.x);
double y = ((LineendPoint.x - center.x) * sin + (LineendPoint.y - center.y) * cos + center.y);
ve[0].x = x;
ve[0].y = y;
double x1 = ((LinestartPoint.x - center.x) * cos - (LinestartPoint.y - center.y) * sin + center.x);
double y1 = ((LinestartPoint.x - center.x) * sin + (LinestartPoint.y - center.y) * cos + center.y);
ve[1].x = x1;
ve[1].y = y1;
return ve;
}
/// <summary>
/// 第一次平移
/// </summary>
/// <param name="dot">交点坐标</param>
/// <param name="center">圆心坐标</param>
/// <returns>返回平移后的点坐标</returns>
internal static Vector2 MoveOne(Vector2 dot, Vector2 center)
{
Vector2 returnDot = new Vector2();
if (center.x >= 0 && center.y >= 0)
{
//圆心在第一象限
returnDot.x = dot.x - center.x;
returnDot.y = dot.y - center.y;
}
else if (center.x <= 0 && center.y >= 0)
{
//圆心在第二象限
returnDot.x = dot.x + center.x;
returnDot.y = dot.y - center.y;
}
else if (center.x <= 0 && center.y <= 0)
{
//圆心在第三象限
returnDot.x = dot.x + center.x;
returnDot.y = dot.y + center.y;
}
else if (center.x >= 0 && center.y <= 0)
{ //圆心在第四象限
returnDot.x = dot.x - center.x;
returnDot.y = dot.y + center.y;
}
return returnDot;
}
/// <summary>
/// 第二次平移
/// </summary>
/// <param name="dot">点坐标</param>
/// <param name="center">圆心坐标</param>
/// <returns>返回平移后的点</returns>
internal static Vector2 MoveTwo(Vector2 dot, Vector2 center)
{
Vector2 returnDot = new Vector2();
if (center.x >= 0 && center.y >= 0)
{
//圆心在第一象限
returnDot.x = dot.x + center.x;
returnDot.y = dot.y + center.y;
}
else if (center.x <= 0 && center.y >= 0)
{
//圆心在第二象限
returnDot.x = dot.x - center.x;
returnDot.y = dot.y + center.y;
}
else if (center.x <= 0 && center.y <= 0)
{
//圆心在第三象限
returnDot.x = dot.x - center.x;
returnDot.y = dot.y - center.y;
}
else if (center.x >= 0 && center.y <= 0)
{ //圆心在第四象限
returnDot.x = dot.x + center.x;
returnDot.y = dot.y - center.y;
}
return returnDot;
}
/// <summary>
/// 判断点是否为(0,0)
/// </summary>
/// <param name="dot">交点</param>
/// <returns>是(0,0)返回false</returns>
internal static bool Zero(Vector2 dot)
{
if (dot.x == 0)
{
if (dot.y == 0)
{
return false;
}
else
{
return true;
}
}
else
{
return true;
}
}
/// <summary>
/// 规整化弧度
/// 返回值范围:[0, 2*PI)
/// </summary>
internal static double NormalizeRadianAngle(double rad)
{
double value = rad % (2 * System.Math.PI);
if (value < 0)
value += 2 * System.Math.PI;
return value;
}