לדלג לתוכן

אלגוריתם כהן-סאת'רלנד

מתוך ויקיפדיה, האנציקלופדיה החופשית

אלגוריתם כהן-סאת'רלנד (Cohen–Sutherland) הוא אלגוריתם בגרפיקה ממוחשבת המשמש ל-Clipping של קווים בשניים או שלושה ממדים. האלגוריתם מחלק את המרחב הדו־ממדי ל-9 חלקים, וב-3 ממדים מחלק ל-27 חלקים ואז קובע באופן יעיל את הקווים והחלקים של הקווים שנמצאים בחלק המרכזי (ה-viewport).

האלגוריתם פותח ב-1967 על ידי דני כהן ואיוואן סאת'רלנד.

האלגוריתם[עריכת קוד מקור | עריכה]

אתחול ושימוש ב־outcodes (בדו מימד) :

ראשית מבצעים חלוקה של 9 אזורים ומקצים זיכרון של 4 ביטים לכל אזור:

4 ביטים
ימין שמאל מעלה מטה

נעדכן כל ביט ל-1 (true) רק אם הוא נמצא ביחס המתאים ל-viewport כשערכו של אזור ה-viewport הוא 0000. לדוגמה: הביט הראשון של האזורים שמימין ל-viewport מאותחל ל-1 (true), ובשאר האזורים מאותחל ל-0 (false), הביט השני מאותחל ל-1 (true), עבור אזורים שמימין ל-viewport, ובשאר האזורים ביט זה מאותחל ל-0 (false), וכך הלאה לשאר הביטים.

שימוש ב־outcodes:

ימין אמצע שמאל
1001 1000 1010 מעלה
0001 0000 0010 אמצע
0101 0100 0110 מטה

אלגוריתם :

האלגוריתם קובע אילו קווים נמצאים ב-viewport על פי ההבחנה הבאה:

  • עבור שני קצוות הקו מתקבל הערך 0000 אזי שניהם מצויים ב-viewport לכן: כל הקו עובר ב-viewport, הקו שייך.
  • עבור שני קצוות הקו יש ביט משותף דולק (לדוגמה: 1000 ו-1010) אזי שני קצוות הקו נמצאים על אותו אזור שאינו ב-viewport לכן: אף חלק מהקו לא עובר ב-viewport, הקו לא שייך.
  • שני הקצוות באזורים שונים (לדוגמה: 0101 ו-1010) אזי אולי חלק מהקו עובר ב-viewport : להבדיל מהמקרים הקודמים שהם טריוואליים. כאן בוחר האלגוריתם את קצה הקו שנמצא באזור שמחוץ ל-viewport (יש לפחות אחד כזה), ומחשב את נקודת החיתוך של הקו מהנקודה החיצונית עם גבולות ה־viewport (באמצעות משוואה פרמטרית של קו) והנקודה החדשה (החיתוך) מחליפה את הנקודה החיצונית ל-viewport. האלגוריתם ממשיך עד להגעה לאחד המקרים הטריוויאלים לעיל.

אלגוריתם זה מוגבל לביצוע clipping במלבנים בלבד. אלגוריתם כללי יותר הפועל על פוליגונים קמורים הוא אלגוריתם Cyrus–Beck.

דוגמת מימוש[עריכת קוד מקור | עריכה]

typedef int OutCode;

const int INSIDE = 0; // 0000
const int LEFT = 1; // 0001
const int RIGHT = 2; // 0010
const int BOTTOM = 4; // 0100
const int TOP = 8; // 1000

// Compute the bit code for a point (x, y) using the clip rectangle
// bounded diagonally by (xmin, ymin), and (xmax, ymax)

// ASSUME THAT xmax, xmin, ymax and ymin are global constants.

OutCode ComputeOutCode(double x, double y)
{
	OutCode code;

	code = INSIDE;          // initialised as being inside of clip window

	if (x < xmin)           // to the left of clip window
		code |= LEFT;
	else if (x > xmax)      // to the right of clip window
		code |= RIGHT;
	if (y < ymin)           // below the clip window
		code |= BOTTOM;
	else if (y > ymax)      // above the clip window
		code |= TOP;

	return code;
}

// Cohen–Sutherland clipping algorithm clips a line from
// P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with 
// diagonal from (xmin, ymin) to (xmax, ymax).
void CohenSutherlandLineClipAndDraw(double x0, double y0, double x1, double y1)
{
	// compute outcodes for P0, P1, and whatever point lies outside the clip rectangle
	OutCode outcode0 = ComputeOutCode(x0, y0);
	OutCode outcode1 = ComputeOutCode(x1, y1);
	bool accept = false;

	while (true) {
		if (!(outcode0 | outcode1)) { // Bitwise OR is 0. Trivially accept and get out of loop
			accept = true;
			break;
		} else if (outcode0 & outcode1) { // Bitwise AND is not 0. Trivially reject and get out of loop
			break;
                } else {
			// failed both tests, so calculate the line segment to clip
			// from an outside point to an intersection with clip edge
			double x, y;

			// At least one endpoint is outside the clip rectangle; pick it.
			OutCode outcodeOut = outcode0? outcode0 : outcode1;

			// Now find the intersection point;
			// use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
			if (outcodeOut & TOP) {           // point is above the clip rectangle
				x = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0);
				y = ymax;
			} else if (outcodeOut & BOTTOM) { // point is below the clip rectangle
				x = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0);
				y = ymin;
			} else if (outcodeOut & RIGHT) {  // point is to the right of clip rectangle
				y = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0);
				x = xmax;
			} else if (outcodeOut & LEFT) {   // point is to the left of clip rectangle
				y = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0);
				x = xmin;
			}

                        //NOTE:*****************************************************************************************

                        /* if you follow this algorithm exactly(at least for c#), then you will fall into an infinite loop 
                        in case a line crosses more than two segments. to avoid that problem, leave out the last else
                        if(outcodeOut & LEFT) and just make it else*/

                        //**********************************************************************************************

			// Now we move outside point to intersection point to clip
			// and get ready for next pass.
			if (outcodeOut == outcode0) {
				x0 = x;
				y0 = y;
				outcode0 = ComputeOutCode(x0, y0);
			} else {
				x1 = x;
				y1 = y;
				outcode1 = ComputeOutCode(x1, y1);
			}
		}
	}
	if (accept) {
               // Following functions are left for implementation by user based on his platform(OpenGL/graphics.h etc.)
               DrawRectangle(xmin, ymin, xmax, ymax);
               LineSegment(x0, y0, x1, y1);
	}
}

קישורים חיצוניים[עריכת קוד מקור | עריכה]