تقاطع قطعتين مستقيمتين

من موسوعة حسوب
اذهب إلى التنقل اذهب إلى البحث
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

تتحقق هذه الخوارزمية من تقاطع قطعتين مستقيمتين (لتكونا (p1, q1) و (p2, q2))، وتعتمد في ذلك على مفهوم التوجيه Orientation.

هناك ثلاثة طرق لتوجيه ثلاث نقاط مرتبة على مستوى معين:

  1. عكس عقارب الساعة
  2. مع عقارب الساعة
  3. على خط واحد colinear

يعرض الشكل التالي طرق التوجيه الثلاثة الواردة في أعلاه للنقاط (a, b, c):

تكون القطعتان المستقيمتان (p1, q1) و (p2, q2) متقاطعتين إذا -وفقط إذا- تحقق أحد الشرطين التاليين:

  1. الحالة العامة:
  • إذا امتلكت النقاط (p1, q1, p2) و (p1, q1, q2) توجيهاً مختلفًا.
  • وإذا امتلكت النقاط (p2, q2, p1) و (p2, q2, q1) توجيهًا مختلفًا.
  1. حالة خاصة:
  • إذا كانت النقاط (p1, q1, p2) و (p1, q1, q2) و (p2, q2, p1) و (p2, q2, q1) على استقامة واحدة.
  • إذا تقاطع الإسقاط على المحور السيني للقطعتين المستقيمتين (p1, q1) و (p2, q2).
  • إذا تقاطع الإسقاط على المحور الصادي للقطعتين المستقيمتين (p1, q1) و (p2, q2).

تنفيذ الخوارزمية

تعرض الأمثلة التالية طريقة تنفيذ الخوارزمية في عدد من لغات البرمجة:

  • C++‎:
#include <iostream> 
using namespace std; 

struct Point 
{ 
	int x; 
	int y; 
}; 

// تتحقق الدالة من أن النقطة المعطاة الأولى تقع على القطعة المستقيمة الناتجة من النقطتين الأخرتين
bool onSegment(Point p, Point q, Point r) 
{ 
	if (q.x <= max(p.x, r.x) && q.x >= min(p.x, r.x) && 
		q.y <= max(p.y, r.y) && q.y >= min(p.y, r.y)) 
	return true; 

	return false; 
} 

// تعيد الدالة القيم التالية بعد تحديد التوجيه الخاص بالنقاط الثلاثة المعطاة
// 0 ---> النقاط الثلاثة على استقامة واحدة
// 1 ---> التوجيه مع عقارب الساعة
// 2 ---> التوجيه عكس عقارب الساعة
int orientation(Point p, Point q, Point r) 
{ 
	int val = (q.y - p.y) * (r.x - q.x) - 
			(q.x - p.x) * (r.y - q.y); 

	if (val == 0) return 0; // على استقامة واحدة

	return (val > 0)? 1: 2; // مع أو عكس عقارب الساعة
} 

// الدالة الرئيسية التي تعيد قيمة صحيحة إذا كانت القطعتان المستقيمتان متقاطعتين

bool doIntersect(Point p1, Point q1, Point p2, Point q2) 
{ 
	// تحديد التوجيهات المطلوبة للحالتين العامة والخاصة
	int o1 = orientation(p1, q1, p2); 
	int o2 = orientation(p1, q1, q2); 
	int o3 = orientation(p2, q2, p1); 
	int o4 = orientation(p2, q2, q1); 

	// الحالة العامة
	if (o1 != o2 && o3 != o4) 
		return true; 

	// الحالات الخاصة
	
	// النقاط p1 و q1 و p2 على استقامة واحدة والنقطة p2 تقع على القطعة المستقيمة p1q1
	if (o1 == 0 && onSegment(p1, p2, q1)) return true; 

	// النقاط p1 و q1 و p2 على استقامة واحدة والنقطة q2 تقع على القطعة المستقيمة p1q1
	if (o2 == 0 && onSegment(p1, q2, q1)) return true; 

	// النقاط p2 و q2 و q1 على استقامة واحدة والنقطة p1 تقع على القطعة المستقيمة p2q2
	if (o3 == 0 && onSegment(p2, p1, q2)) return true; 

	// النقاط p2 و q2 و q1 على استقامة واحدة والنقطة q1 تقع على القطعة المستقيمة p2q2
	if (o4 == 0 && onSegment(p2, q1, q2)) return true; 

	return false; // لا تنتمي النقاط إلى أيٍّ من الحالات السابقة
} 

// اختبار الدوال السابقة 
int main() 
{ 
	struct Point p1 = {1, 1}, q1 = {10, 1}; 
	struct Point p2 = {1, 2}, q2 = {10, 2}; 

	doIntersect(p1, q1, p2, q2)? cout << "Yes\n": cout << "No\n"; 

	p1 = {10, 0}, q1 = {0, 10}; 
	p2 = {0, 0}, q2 = {10, 10}; 
	doIntersect(p1, q1, p2, q2)? cout << "Yes\n": cout << "No\n"; 

	p1 = {-5, -5}, q1 = {0, 0}; 
	p2 = {1, 1}, q2 = {10, 10}; 
	doIntersect(p1, q1, p2, q2)? cout << "Yes\n": cout << "No\n"; 

	return 0; 
}
  • بايثون:
  • جافا:
class GFG 
{ 

static class Point 
{ 
	int x; 
	int y; 

		public Point(int x, int y) 
		{ 
			this.x = x; 
			this.y = y; 
		} 
	
}; 

// تتحقق الدالة من أن النقطة المعطاة الأولى تقع على القطعة المستقيمة الناتجة من النقطتين الأخرتين
static boolean onSegment(Point p, Point q, Point r) 
{ 
	if (q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && 
		q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y)) 
	return true; 

	return false; 
} 

// تعيد الدالة القيم التالية بعد تحديد التوجيه الخاص بالنقاط الثلاثة المعطاة
// 0 ---> النقاط الثلاثة على استقامة واحدة
// 1 ---> التوجيه مع عقارب الساعة
// 2 ---> التوجيه عكس عقارب الساعة
static int orientation(Point p, Point q, Point r) 
{ 
	int val = (q.y - p.y) * (r.x - q.x) - 
			(q.x - p.x) * (r.y - q.y); 

	if (val == 0) return 0; // على استقامة واحدة 

	return (val > 0)? 1: 2; // مع أو عكس عقارب الساعة 
} 

// الدالة الرئيسية التي تعيد قيمة صحيحة إذا كانت القطعتان المستقيمتان متقاطعتين
static boolean doIntersect(Point p1, Point q1, Point p2, Point q2) 
{ 
	// تحديد التوجيهات المطلوبة للحالتين العامة والخاصة 
	int o1 = orientation(p1, q1, p2); 
	int o2 = orientation(p1, q1, q2); 
	int o3 = orientation(p2, q2, p1); 
	int o4 = orientation(p2, q2, q1); 

	// الحالة العامة 
	if (o1 != o2 && o3 != o4) 
		return true; 

	// الحالات الخاصة 
	// النقاط p1 و q1 و p2 على استقامة واحدة والنقطة p2 تقع على القطعة المستقيمة p1q1 
	if (o1 == 0 && onSegment(p1, p2, q1)) return true; 

	// النقاط p1 و q1 و p2 على استقامة واحدة والنقطة q2 تقع على القطعة المستقيمة p1q1 
	if (o2 == 0 && onSegment(p1, q2, q1)) return true; 

	// النقاط p2 و q2 و q1 على استقامة واحدة والنقطة p1 تقع على القطعة المستقيمة p2q2 
	if (o3 == 0 && onSegment(p2, p1, q2)) return true; 

	// النقاط p2 و q2 و q1 على استقامة واحدة والنقطة q1 تقع على القطعة المستقيمة p2q2 
	if (o4 == 0 && onSegment(p2, q1, q2)) return true; 

	return false; // لا تنتمي النقاط إلى أيٍّ من الحالات السابقة
} 

// اختبار الدوال السابقة
public static void main(String[] args) 
{ 
	Point p1 = new Point(1, 1); 
	Point q1 = new Point(10, 1); 
	Point p2 = new Point(1, 2); 
	Point q2 = new Point(10, 2); 

	if(doIntersect(p1, q1, p2, q2)) 
		System.out.println("Yes"); 
	else
		System.out.println("No"); 

	p1 = new Point(10, 1); q1 = new Point(0, 10); 
	p2 = new Point(0, 0); q2 = new Point(10, 10); 
	if(doIntersect(p1, q1, p2, q2)) 
			System.out.println("Yes"); 
	else
		System.out.println("No"); 

	p1 = new Point(-5, -5); q1 = new Point(0, 0); 
	p2 = new Point(1, 1); q2 = new Point(10, 10);; 
	if(doIntersect(p1, q1, p2, q2)) 
		System.out.println("Yes"); 
	else
		System.out.println("No"); 
} 
}

تعطي الشيفرات السابقة المخرجات التالية:

No
Yes
No

مصادر