I am participating in the individual competition of the Nuggets Community Game Creativity Contest. For details, please see: Game Creativity Contest
main content:
- Dot product and cross product
- Determine the positional relationship between the point and the line
- Determine if two line segments intersect and calculate the intersection
- Determine the coincidence of two line segments and calculate their coincident parts
- todo merges two overlapping line segments
Dot product (dot product)
- Calculation formula: a·b = (ax,ay)·(bx,by) = axbx+ayby = |a|·|b|·cosθ; the result is a scalar.
- Compute the projection of vector b on vector a (unit vector): projection length = |b| cosθ; projection = a |b| cosθ
- Calculate the angle between two vectors: cosθ = a·b / (|a|·|b|) => θ = arccos(a·b / (|a|·|b|))
- The larger the result of the dot product, the smaller the angle between the two vectors; it can be used to judge the size of the angle.
- Dot product result == 1: included angle θ == 0;
- Dot product result > 0: 0 <= included angle θ < 90;
- Dot product result == 0: included angle θ == 90;
- Dot product result < 0: 90 < angle θ < 180;
- Dot product result == -1: Angle θ == 190.
Cross product (cross product)
- The calculation formula is: a×b=(ax,ay,az)×(bx,by,bz)=(aybz-azby,azbx-axbz,axby-aybx).
- The result is a vector perpendicular to a and b, which can be used to determine the relative position of vectors a and b (clockwise or counterclockwise).
- |a×b| = |a|·|b|·sinθ = area of a parallelogram with a and b as sides
Position relationship between point and line
Known conditions: point M, line segment l (end points A, B), determine the positional relationship between the point and the line
Calculation method one:
Straight line formula: a * X + b * Y + c = 0
Derived formula:
- Substitute the segment endpoints into the line formula:
a * Ax + b * Ay + c = 0
a * Bx + b * By + c = 0 - 两式分别相加、相减:
(A.x + B.x) * a + (A.y + B.y) * b + 2c = 0
(A.x - B.x) * a + (A.y - B.y) * b = 0 - 公式化简得:
b = (B.x - A.x) / (A.y - B.y) * a
c = -a * (A.y * B.x - A.x * B.y) / (A.y - B.y) - 原直线公式用a表示:
a * X + (B.x - A.x) / (A.y - B.y) * a * Y - a * (A.y * B.x - A.x * B.y) / (A.y - B.y) = 0 - 公式两边同时除a,直线公式用点A/B表示:
X + (B.x - A.x) / (A.y - B.y) * Y - (A.y * B.x - A.x * B.y) / (A.y - B.y) = 0 - 继续化简:
(A.y - B.y) * X + (B.x - A.x) * Y - (A.y * B.x - A.x * B.y) = 0 - 公式左侧==0,点在直线上,公式左侧>0,点在直线右侧,公式左侧<0,点在直线左侧。
- 将目标点M代入公式:
(A.y - B.y) * M.x + (B.x - A.x) * M.y - (A.y * B.x - A.x * B.y)
A.y * M.x - B.y * M.x + B.x * M.y - A.x * M.y - A.y * B.x + A.x * B.y - 最终整理:
(B.y - M.y) * (A.x - M.x) - (A.y - M.y) * (B.x - M.x)
代码实现:
//点与直线的位置,目标点M,直线AB
public float PointOfLine(Vector2 M,Vector2 A,Vector2 B)
{
// 返回值 > 0 在右侧, = 0 在线上, < 0 在左侧
return (B.y - M.y) * (A.x - M.x) - (A.y - M.y) * (B.x - M.x);
}
//判断点是否在线段上,目标点M,线段AB
public bool IsPointOnLine(Vector2 M,Vector2 A,Vector2 B)
{
return IsZero(PointOfLine(M,A,B));
}
复制代码
计算方式二:
点直线段位置判断步骤:
- 点M与端点A或B重合;
- 线段没有斜率或斜率为0,判断M是否在直线AB上,在判断是否在线段内;
- 线段有斜率,判断线段MA与BM斜率是否一致
代码实现:
// 判断点是否在线段上,目标点M,线段AB
public bool IsPointOnLine(Vector2 M,Vector2 A,Vector2 B)
{
//与端点重合
if(M == A || M == B)
return true;
//在同一竖直方向,线段竖直,点在该线段所在的直线上
if(IsZero(A.x - B.x) && IsZero(A.x - M.x))
{
//已判定点在直线上,若点在两端点中间,即点在线段上
//if ((M.y < B.y && M.y > A.y) || (M.y < A.y && M.y > B.y)),化简为:
if((A.y - M.y) * (M.y - B.y) > 0.0f)
{
return true;
}
return false;
}
//在同一水平方向
else if(IsZero(A.y - B.y) && IsZero(A.y - M.y))
{
if((A.x - M.x) * (M.x - B.x) > 0.0f)
{
return true;
}
return false;
}
//线段倾斜,此时线段所在直线存在斜率
else
{
//点在直线上,MA与MB斜率相等,且有共同点M,此时MA与MB重合,即点M在直线AB上
if(IsZero((A.y - M.y) / (A.x - M.x) - (M.y - B.y) / (M.x - B.x)))
{
//已判定点在直线上,若点在两端点中间,即点在线段上
if(((A.y - M.y) * (M.y - B.y) > 0) && ((A.x - M.x) * (M.x - B.x) > 0))
{
return true;
}
}
return false;
}
}
复制代码
判断两线段是否相交
已知条件:线段l1(端点A、B)、线段l2(端点C、D)
两线段位相交关系(不考虑重叠的情况,如果线段重叠,也只返回一个点):
- 完全相交:线段AB的端点分别在线段CD的两侧,且线段CD的端点也分别在线段AB的两侧;
- 不完全相交:线段一个端点在另一条线段上(比如:端点A在线段CD上,如果端点B也在视为重叠,暂不区分重叠的情况)
- 不相交:线段的两个端点都在另一条线段的同一侧
先计算每个端点与直线的位置关系,在判断段线段的位置关系
代码实现:
//判断两线段交叉并计算交叉点,线段AB,线段CD
public Vector2? LineCross(Vector2 A,Vector2 B,Vector2 C,Vector2 D)
{
//计算各端点与线段的关系
float pointPosA = PointOfLine(A,C,D);
float pointPosB = PointOfLine(B,C,D);
float pointPosC = PointOfLine(C,A,B);
float pointPosD = PointOfLine(D,A,B);
//完全相交:线段AB的端点分别在线段CD的两侧,且线段CD的端点也分别在线段AB的两侧
if(pointPosA * pointPosB < 0 && pointPosC * pointPosD < 0)
{
return GetCrosPoint(A,B,C,D);
}
//不完全相交:线段一个端点在另一条线段上
if(IsZero(pointPosA))
return A;
if(IsZero(pointPosB))
return B;
if(IsZero(pointPosC))
return C;
if(IsZero(pointPosD))
return D;
//若不符合以上条件,则不相交
//判断条件:pointPosA * pointPosB > 0 || pointPosC * pointPosD > 0
return null;
}
复制代码
计算两条相交线段的交点
直线方程:Y = aX + b
相交线段分情况处理(前提是已经确定线段相交):
- if 两线段都没有斜率,或者两斜率相等,此时两线段平行
- 不存在交点
- else if 只有一条线段没有斜率(竖直方向的线段没有斜率)
- 方法一:解方程式
- 计算公式(假设线段AB不存在斜率,线段CD存在斜率):
- 线段AB的直线方程:直线不存在斜率,X = Ax
- 线段CD的直线方程:
- 根据已有两点计算斜率:a2 = (D.y - C.y) / (D.x - C.x)
- 截距:b = Y - aX = (C.y - C.x * a2)
- 将斜率带入获得直线公式:Y = a2 * X + (C.y - C.x * a2)
- 根据两个直线公式计算交点
- x = A.x;
- y = a2 * (A.x - C.x) + C.y;
- 方式二:三角形三边关系
- 假设线段AB不存在斜率,线段CD存在斜率,交点为M
- 交点M在线段AB上,其x坐标与线段任意一点x坐标相同
- 交点M在线段CD上,CM与CD在同一条直线上,两线段斜率相等
- (M.y - C.y) / (M.x - C.x) = a2
- M.y = a2 * (M.x - C.x) + C.y
- M.x = A.x
- M.y = a2 * (A.x - C.x) + C.y
- else both line breaks have slopes
- Calculate the slope and intercept of the line where the two line segments lie; solve the system of equations.
Code:
//计算两线段交点,线段AB,线段CD
public Vector2? GetCrosPoint(Vector2 A,Vector2 B,Vector2 C,Vector2 D)
{
//直线方程: Y = aX + b
if(IsZero((A.x - B.x) - (C.x - D.x)))
{
//两线段都没有斜率,或者两斜率相等,此时两线段平行
return null;
}
else if(IsZero(A.x - B.x))
{
//线段AB竖直方向,交点为M
//方法一:解方程式
//线段AB的直线方程:直线不存在斜率,X = Ax
//线段CD的直线方程:
//根据已有两点计算斜率:a2 = (D.y - C.y) / (D.x - C.x)
//截距:b = Y - aX = (C.y - C.x * a2)
//将斜率带入获得直线公式:Y = a2 * X + (C.y - C.x * a2)
//根据两个直线公式计算交点
//x = A.x; y = a2 * (A.x - C.x) + C.y;
//方法二:三角形关系(不推荐)
//计算过程如下:
//交点M在线段AB上,其x坐标与线段任一点x坐标相同
//交点M在线段CD上,CM与CD在同一条直线上,两线段斜率相等
// (M.y - C.y) / (M.x - C.x) = a2
// M.y = a2 * (M.x - C.x) + C.y
// M.x = A.x
// M.y = a2 * (A.x - C.x) + C.y
//最终均可得
float a2 = (D.y - C.y) / (D.x - C.x);
float x = A.x;
float y = a2 * (A.x - C.x) + C.y;
return new Vector2(x,y);
}
else if(IsZero(C.x - D.x))
{
//线段CD竖直方向,计算过程同上
float x = C.x;
float a1 = (B.y - A.y) / (B.x - A.x);
float y = a1 * (C.x - A.x) + A.y;
return new Vector2(x,y);
}
else
{
//线段都有斜率,且不相等
//解方程式,(方程式由公式Y = aX + b代入两点参数得)
// Y = a1 * X + (A.y - A.x * a1)
// Y = a2 * X + (C.y - C.x * a2)
float a1 = (B.y - A.y) / (B.x - A.x);
float a2 = (D.y - C.y) / (D.x - C.x);
float x = (a1 * A.x - a2 * C.x - A.y + C.y) / (a1 - a2);
float y = a1 * x - a1 * A.x + A.y;
return new Vector2(x,y);
}
}
复制代码
Determine whether two line segments are coincident, and calculate the coincident part
Line segment coincidence:
- Inclusion and coincidence: a line segment is contained in another line segment, for example: the endpoint A and endpoint B of the line segment AB are both on the line segment CD;
- Cross coincidence: the endpoint A of the line segment AB is on the line segment CD, the endpoint C of the line segment CD is on the line segment AB, and AC is not the same point;
- Non-polymerization.
Determine the coincidence of two line segments and calculate their coincident parts, line segment AB, line segment CD (the coincident part is a line segment, return the two endpoints of the coincident line segment, regardless of the case where there is only one coincident point):
public Vector2[] LineCoincide(Vector2 A,Vector2 B,Vector2 C,Vector2 D)
{
//可能情况说明:
//01 包含关系,一条线段包含在另一条线段内,比如:线段AB的端点A和端点B都在线段CD上,或两条线段的两个端点完全重叠
// onLineA && onLineB
// onLineC && onLineD
//02 交叉关系,各包含一个端点,比如:线段AB的端点A在线段CD上,线段CD的端点C在线段AB上,或两条线段的各一个端点重叠
// onLineA && onLineC && A != C
// onLineA && onLineD && A != D
// onLineB && onLineC && B != C
// onLineB && onLineD && B != D
//计算各个端点与线段的关系(点是否在线段上)
bool onLineA = IsPointOnLine(A,C,D);
bool onLineB = IsPointOnLine(B,C,D);
bool onLineC = IsPointOnLine(C,A,B);
bool onLineD = IsPointOnLine(D,A,B);
//coincide01
if(onLineA && onLineB)
return new Vector2[] { A,B };
else if(onLineC && onLineD)
return new Vector2[] { C,D };
//coincide02
if(onLineA && onLineC && (A != C))
return new Vector2[] { A,C };
else if(onLineA && onLineD && (A != D))
return new Vector2[] { A,D };
else if(onLineB && onLineC && (B != C))
return new Vector2[] { B,C };
else if(onLineB && onLineD && (B != D))
return new Vector2[] { B,D };
//若不符合以上条件,则不重合
return null;
}
复制代码
Only judge whether the two line segments coincide, line segment AB, line segment CD
public bool IsLineCoincide(Vector2 A,Vector2 B,Vector2 C,Vector2 D)
{
bool onLineA = IsPointOnLine(A,C,D);
bool onLineB = IsPointOnLine(B,C,D);
bool onLineC = IsPointOnLine(C,A,B);
bool onLineD = IsPointOnLine(D,A,B);
if((onLineA && onLineB) || (onLineC && onLineD))
return true;
if((onLineA && onLineC && (A != C))
|| (onLineA && onLineD && (A != D))
|| (onLineB && onLineC && (B != C))
|| (onLineB && onLineD && (B != D)))
return true;
return false;
}
复制代码
//判断float == 0
bool IsZero(float floatValue)
{
if(floatValue > -0.00001f & floatValue < 0.00001f)
return true;
else
return false;
}
复制代码