Unity中判断平面上两条线段是否有交点及计算交点位置代码参考

判断是否相交

一般的写法

参考如下:

bool IsSegmentsIntersect(Vector2 a, Vector2 b, Vector2 c, Vector2 d)
{
	Vector2 ab = b - a;
	Vector2 ac = c - a;
	Vector2 ad = d - a;
	Vector2 cd = d - c;
	Vector2 ca = a - c;
	Vector2 cb = b - c;

	float crossAB_AC = CrossProduct(ab, ac);
	float crossAB_AD = CrossProduct(ab, ad);
	float crossCD_CA = CrossProduct(cd, ca);
	float crossCD_CB = CrossProduct(cd, cb);

	// Check general case
	if (crossAB_AC * crossAB_AD < 0 && crossCD_CA * crossCD_CB < 0) return true;

	// Check special cases where points are collinear
	if (Mathf.Approximately(crossAB_AC, 0) && IsPointOnSegment(c, a, b)) return true;
	if (Mathf.Approximately(crossAB_AD, 0) && IsPointOnSegment(d, a, b)) return true;
	if (Mathf.Approximately(crossCD_CA, 0) && IsPointOnSegment(a, c, d)) return true;
	if (Mathf.Approximately(crossCD_CB, 0) && IsPointOnSegment(b, c, d)) return true;

	return false;

	float CrossProduct(Vector2 a, Vector2 b) { return a.x * b.y - a.y * b.x; }

	bool IsPointOnSegment(Vector2 p, Vector2 a, Vector2 b) { return Mathf.Min(a.x, b.x) <= p.x && p.x <= Mathf.Max(a.x, b.x) && Mathf.Min(a.y, b.y) <= p.y && p.y <= Mathf.Max(a.y, b.y); }
}

优化写法一

        但是有些时候可能我们需要判断大量的线段互相之间是否相交,而且大部分情况是不相交的,我门希望能够快速判断出不相交的结果,这时候需要使用包围盒来提升速度,代码参考如下:

bool IsSegmentsIntersect(Vector2 a, Vector2 b, Vector2 c, Vector2 d)
{
	if (!BoundingBoxCheck(a, b, c, d)) return false;

	Vector2 ab = b - a;
	Vector2 ac = c - a;
	Vector2 ad = d - a;
	Vector2 cd = d - c;
	Vector2 ca = a - c;
	Vector2 cb = b - c;

	float crossAB_AC = CrossProduct(ab, ac);
	float crossAB_AD = CrossProduct(ab, ad);
	float crossCD_CA = CrossProduct(cd, ca);
	float crossCD_CB = CrossProduct(cd, cb);

	if (crossAB_AC * crossAB_AD < 0 && crossCD_CA * crossCD_CB < 0)return true;

	if (Mathf.Approximately(crossAB_AC, 0) && IsPointOnSegment(c, a, b)) return true;
	if (Mathf.Approximately(crossAB_AD, 0) && IsPointOnSegment(d, a, b)) return true;
	if (Mathf.Approximately(crossCD_CA, 0) && IsPointOnSegment(a, c, d)) return true;
	if (Mathf.Approximately(crossCD_CB, 0) && IsPointOnSegment(b, c, d)) return true;

	return false;

	bool BoundingBoxCheck(Vector2 a, Vector2 b, Vector2 c, Vector2 d) { return Mathf.Max(a.x, b.x) >= Mathf.Min(c.x, d.x) && Mathf.Max(c.x, d.x) >= Mathf.Min(a.x, b.x) && Mathf.Max(a.y, b.y) >= Mathf.Min(c.y, d.y) && Mathf.Max(c.y, d.y) >= Mathf.Min(a.y, b.y); }

	float CrossProduct(Vector2 a, Vector2 b) { return a.x * b.y - a.y * b.x; }

	bool IsPointOnSegment(Vector2 p, Vector2 a, Vector2 b) { return Mathf.Min(a.x, b.x) <= p.x && p.x <= Mathf.Max(a.x, b.x) && Mathf.Min(a.y, b.y) <= p.y && p.y <= Mathf.Max(a.y, b.y); }
}

优化写法二

        下面是快速判断不相交的另一种写法,思想上差不多,代码稍微简洁一点儿:

	bool IsSegmentsIntersect(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4)
	{
		//首先检查以两条线段为对角线的两个矩形是否相交
		//对于线段p1p2和线段p3p4,分别求出两条线段在x和y轴方向上的最大最小值
		float maxX1 = Mathf.Max(p1.x, p2.x);
		float minX1 = Mathf.Min(p1.x, p2.x);
		float maxY1 = Mathf.Max(p1.y, p2.y);
		float minY1 = Mathf.Min(p1.y, p2.y);

		float maxX2 = Mathf.Max(p3.x, p4.x);
		float minX2 = Mathf.Min(p3.x, p4.x);
		float maxY2 = Mathf.Max(p3.y, p4.y);
		float minY2 = Mathf.Min(p3.y, p4.y);

		//如果满足以下两个条件,则可能相交,否则一定不相交
		//线段p1p2的最大x值大于等于线段p3p4的最小x值
		//线段p1p2的最小x值小于等于线段p3p4的最大x值
		//线段p1p2的最大y值大于等于线段p3p4的最小x值
		//线段p1p2的最小y值小于等于线段p3p4的最大x值
		if (maxX1 < minX2 || maxX2 < minX1 || maxY1 < minY2 || maxY2 < minY1) return false;

		//如果向量p1-p3和p2-p3的叉乘结果与向量p2-p1和p4-p1的叉乘结果异号,则说明点p3和p4在'直线'p1p2的两侧。
		//同理判断点p1和p2在'直线'p3p4的两侧
		float crossProduct1 = (p3.x - p1.x) * (p2.y - p1.y) - (p3.y - p1.y) * (p2.x - p1.x);
		float crossProduct2 = (p4.x - p1.x) * (p2.y - p1.y) - (p4.y - p1.y) * (p2.x - p1.x);

		float crossProduct3 = (p1.x - p3.x) * (p4.y - p3.y) - (p1.y - p3.y) * (p4.x - p3.x);
		float crossProduct4 = (p2.x - p3.x) * (p4.y - p3.y) - (p2.y - p3.y) * (p4.x - p3.x);

		return crossProduct1 * crossProduct2 <= 0 && crossProduct3 * crossProduct4 <= 0;
	}

计算交点位置

        如果已知两个二维线段必定相交,可通过以下方法计算其交点位置。 

 

        代码参考如下:

	Vector2 GetIntersectionPoint(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4)
	{
		float x1 = p1.x, y1 = p1.y;
		float x2 = p2.x, y2 = p2.y;
		float x3 = p3.x, y3 = p3.y;
		float x4 = p4.x, y4 = p4.y;

		float denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);

		float t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denominator;

		return new Vector2(x1 + t * (x2 - x1), y1 + t * (y2 - y1));
	}

猜你喜欢

转载自blog.csdn.net/ttod/article/details/142627164