基於Weiler-Atherton剪裁布爾運算openGL實現-2D

#pragma once

#ifndef LINE_H
#define LINE_H
#include "Point2.h"
class Line
{
public:
	Line() {};
	~Line() {};
	Line(Point2 A, Point2 B)
		:First(A), Second(B)
	{};
	Point2 First, Second;

};

#endif

#pragma once

#ifndef POINT2_H
#define POINT2_H

class Point2 {
public:
	Point2() {};
	~Point2() {};

	Point2(double x1, double y1)
		:x(x1), y(y1)
	{};
	void set(double x1, double y1)
	{
		x = x1;
		y = y1;
	}
	double x, y;

	Point2 operator - (const Point2 v) const
	{
		return Point2(x - v.x, y - v.y);
	}

	Point2 operator + (const Point2 v) const
	{
		return Point2(x + v.x, y + v.y);
	}

	Point2 operator * (const double k) const
	{
		return Point2(x * k, y * k);
	}


	double dot(const Point2 v) const
	{
		return x*v.x + y*v.y;
	}

	Point2 Vertical() const
	{
		return Point2(y, -x);
	}
};

#endif // !POINT2_H

聲明
/*
* 實現二維平面的布爾運算
* Union 並集 intersection 交集 Subtract 減
*/
#pragma once

#ifndef BOOLEAN_H
#define BOOLEAN_H

#include 
#include 
#include 
#include   
#include 

#include "Point2.h"
#include "Line.h"
using namespace std;

class Boolean
{
public:
	Boolean();
	~Boolean();

	void SetPLane(list A, list B); //輸入list數據後 先計算出所有交點 並保存在AList BList
	void BoolIntersection(list &PointInter, list &Num);  //輸出交集的座標 PointInter表示交集座標 Num的值表示交集面的座標個數 Num的size表示交集面的個數
	void Display();

private:

	bool IsInsect(const Point2 A, const Point2 B, const Point2 C,const Point2 D,
		Point2 &Insect, double &t);  //  求直線AB與線段CD交點 若有交點返回true


	struct InPoint
	{
		Point2 Point;
		int IsIn; //1 爲交點 0 爲端點
	};

	list AList, BList;
};

#endif // !BOOLEAN_H

實現
#include"Boolean.h"

Boolean::Boolean()
{

}

Boolean::~Boolean()
{

}

void Boolean::SetPLane(list A, list B)
{
	list::iterator ita, itb;
	vector t; //保存t
	Point2 tempP2;
	double tempt;
	for (ita = A.begin(); ita != A.end(); ita++)
	{
		for (itb = B.begin(); itb != B.end(); itb++)
		{			
			if (IsInsect(ita->First, ita->Second, itb->First, itb->Second, tempP2, tempt))
			{
				t.push_back(tempt);
			}
		}
		sort(t.begin(), t.end());
		InPoint tempInp;
		//tempInp.Point = ita->First;
		//tempInp.IsIn = 0;
		//AList.push_back(tempInp);
		int Nt = 0;
		int Nt_In = 0;
		for (int i = 0; i < t.size(); i++)
		{
			Nt_In++;// 判斷交點是出去還是進入 被2整除出 餘1進

			if (t[i] <= 1) Nt++; //統計小於1的個數 用於判斷B點是否在內部 被2整除不在 餘1在

			if (t[i] > 0 && t[i] < 1)
			{
				tempInp.Point = ita->First + (ita->Second - ita->First) * t[i];

				if(Nt_In % 2 == 0) tempInp.IsIn = 2; // 交點
				else tempInp.IsIn = 1; // 交點

				AList.push_back(tempInp);
			}
		}
		tempInp.Point = ita->Second;
		if(Nt % 2 == 0) tempInp.IsIn = 0;
		else tempInp.IsIn = 1;
		AList.push_back(tempInp);
		t.clear();
	}

	for (itb = B.begin(); itb != B.end(); itb++)
	{
		for (ita = A.begin(); ita != A.end(); ita++)
		{
			if (IsInsect(itb->First, itb->Second, ita->First, ita->Second, tempP2, tempt))
			{
				t.push_back(tempt);
			}
		}
		sort(t.begin(), t.end());
		InPoint tempInp;
		int Nt = 0;
		int Nt_In = 0;
		for (int i = 0; i < t.size(); i++)
		{
			Nt_In++;
			if (t[i] <= 1) Nt++; //統計小於1的個數 用於判斷B點是否在內部 被2整除不在 餘1在

			if (t[i] > 0 && t[i] < 1)
			{
				tempInp.Point = itb->First + (itb->Second - itb->First) * t[i];
				if (Nt_In % 2 == 0) tempInp.IsIn = 2; // 交點
				else tempInp.IsIn = 1; // 交點
				BList.push_back(tempInp);
			}
		}
		tempInp.Point = itb->Second;
		if (Nt % 2 == 0) tempInp.IsIn = 0;
		else tempInp.IsIn = 1;
		BList.push_back(tempInp);
		t.clear();
	}
}

void Boolean::BoolIntersection(list &PointInter, list &Num)
{
	list::iterator ita;
	list::iterator itb;
	list::iterator ittemp_back;
	list::iterator itb_back;
	list::iterator ittemp;
	int JumpA = 1 ; // 跳轉 1表示跳到A  0表示調到B
	int State = 0;  // 0表示在遍歷 A 
	ita = AList.begin();
	//先遍歷AList 然後遍歷BList 
	while(1)
	{
		if (ita->IsIn == 1)
		{
			int NPoint = 0;
			PointInter.push_back(ita->Point);
			NPoint++;
			double a = ita->Point.x;
			double b = ita->Point.y;
			ittemp_back = ita;
			ita++;
			ittemp = ita;

			if (JumpA == 0) JumpA = 1;	
			else JumpA = 0;
	
			while(abs(a - ittemp->Point.x) > 0.001 || abs(b -ittemp->Point.y) > 0.001)
			{
				if (ittemp->IsIn == 1)
				{
					PointInter.push_back(ittemp->Point);
					ittemp->IsIn = 3; // 標記已經pick的點
					NPoint++;
					ittemp_back = ittemp;
					ittemp++;
				}//endif
				else if (ittemp->IsIn == 2)//跳轉
				{
					PointInter.push_back(ittemp->Point);
					ittemp->IsIn = 3; // 標記已經pick的點
					NPoint++;
					ittemp_back = ittemp;

					if (JumpA == 0) {
						ittemp = BList.begin();
						JumpA = 1;
					}
					else
					{
						ittemp = AList.begin();
						JumpA = 0;
					}

					while (abs(ittemp->Point.x - ittemp_back->Point.x) > 0.001 || abs(ittemp->Point.y -ittemp_back->Point.y) > 0.001)
					{
						ittemp++;
					}
					ittemp->IsIn = 3; // 標記已經pick的點
					ittemp++;
					if (ittemp->IsIn == 0)
						break; // 交點爲一個的情況
				}//endelse

				if (JumpA == 0) {
					if (ittemp == AList.end())
						ittemp = AList.begin();
				}
				else
				{
					if (ittemp == BList.end())
						ittemp = BList.begin();
				}

			} //endwhile

			Num.push_back(NPoint);
			NPoint = 0; //重新計數

		}//endif

		ita++;
		if (State == 0)
		{
			if (ita == AList.end())
			{
				ita = BList.begin();
				JumpA = 1;
				State = 1;
			}
		}
		else
		{
			if (ita == BList.end())
				break;
		}

	}//endwhile
}

void Boolean::Display()
{
	list::iterator it;
	for (it = AList.begin(); it != AList.end(); it++)
	{
		cout << it->Point.x << " " << it->Point.y << " " << it->IsIn << "\n";
	}
	cout << "\n";
	list::iterator itb;
	for (itb = BList.begin(); itb != BList.end(); itb++)
	{
		cout << itb->Point.x << " " << itb->Point.y << " " << itb->IsIn << "\n";
	}


}

bool Boolean::IsInsect(const Point2 A, const Point2 B, const Point2 C, const Point2 D, Point2 &Insect, double &t)
{
	Point2 b = B - A; //向量b
	Point2 d = D - C;
	Point2 c = C - A;

	Point2 d_V = d.Vertical(); //d的垂線

	if (b.dot(d_V) == 0)
		return false;
	else
	{
		t = c.dot(d_V) / b.dot(d_V);
		Point2 b_V = b.Vertical();
		double u = c.dot(b_V) / b.dot(d_V);
		if (u < 0 || u > 1)
			return false;
		else
		{
			Insect = A + b * t;
			return true;
		}
	}
	
}

測試程序按b後繪製新的折線繪製兩組折線後點u調用Boolean交集運算,將交集繪製成紅色
#include
#include
#include
#include

#include"Boolean.h"

using namespace std;

const int ScreenWidth = 640;
const int ScreenHeight = 480;
int N_Line = -1;
unsigned char Key;
const int Num = 20;

std::list xList[Num];
std::list yList[Num];

void Init(void)
{
	glClearColor(1.0, 1.0, 1.0, 0.0);
	glColor3f(0.0f, 0.0f, 0.0f);
	glPointSize(2.0);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0.0, (GLdouble)ScreenWidth, 0.0, (GLdouble)ScreenHeight);
}

void myMouse(int button, int state, int x, int y) //繪製折線
{
	if ((button == GLUT_LEFT_BUTTON) && (state == GLUT_DOWN) && (Key == 'b'))
	{
		//std::cout << "111" << "\n";
		xList[N_Line].push_back(x);
		yList[N_Line].push_back(ScreenHeight - y);
		glClear(GL_COLOR_BUFFER_BIT);
		std::list::iterator itx;
		std::list::iterator ity;
		for (int j = 0; j <= N_Line; j++)
		{
			glBegin(GL_LINE_LOOP);
			itx = xList[j].begin();
			ity = yList[j].begin();
			for (itx = xList[j].begin(); itx != xList[j].end(); itx++)
			{
				glVertex2i(*itx, *ity);
				ity++;
			}
			glEnd();
		}
		glFlush();
	}
}

void Uion(void)
{
	list AList;
	list BList;
	list::iterator itx;
	list::iterator ity;
	itx = xList[0].begin();
	ity = yList[0].begin();
	double xA = *itx;
	double yA = *ity;
	Point2 A(xA, yA), B;
	itx++;
	ity++;
	for (itx; itx != xList[0].end(); itx++)
	{
		B.set(*itx, *ity);
		AList.push_back(Line(A, B));
		A.set(*itx, *ity);
		ity++;
	}
	B.set(xA, yA);
	AList.push_back(Line(A, B));


	itx = xList[1].begin();
	ity = yList[1].begin();
	xA = *itx;
    yA = *ity;
    A.set(xA, yA);
	itx++;
	ity++;
	for (itx; itx != xList[1].end(); itx++)
	{
		//Point2 A(xA, yA);
		B.set(*itx, *ity);
		BList.push_back(Line(A, B));
		A.set(*itx, *ity);
		ity++;
	}
	B.set(xA, yA);
	BList.push_back(Line(A, B));


	list::iterator itL;
	for (itL = AList.begin(); itL != AList.end(); itL++)
	{
		cout << itL->First.x << " " << itL->First.y << " " << itL->Second.x << " " << itL->Second.y << "\n";
	}

	for (itL = BList.begin(); itL != BList.end(); itL++)
	{
		cout << itL->First.x << " " << itL->First.y << " " << itL->Second.x << " " << itL->Second.y << "\n";
	}


	Boolean bln;
	bln.SetPLane(AList, BList);
	bln.Display();

	list PointInter;
	list Num;
	bln.BoolIntersection(PointInter, Num);

	list::iterator it;
	list::iterator itN;

	glColor3f(1.0f, 0.0f, 0.0f);


	it = PointInter.begin();
	for (itN = Num.begin(); itN != Num.end(); itN++)
	{
		glBegin(GL_LINE_LOOP);
		for (int i = 0; i < *itN; i++)
		{
			glVertex2d(it->x, it->y);
			it++;
		}
		glEnd();
	}

	glFlush();
}


void myKeyboard(unsigned char theKey, int mouseX, int mouseY)
{
	GLint x = mouseX;
	GLint y = ScreenHeight - mouseY;
	//Key = theKey;
	switch (theKey)
	{
	case 'b':
		Key = theKey;
		N_Line++;
		break;
	case 'u':
		Uion();
		break;
	default:
		break;
	}
}

void myDisplay(void)
{
}
void main(int argc, char * argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);

	glutInitWindowPosition(100, 150);
	glutInitWindowSize(ScreenWidth, ScreenHeight);
	glutCreateWindow("First_GL!");

	Init();
	glutDisplayFunc(myDisplay);

	glutKeyboardFunc(myKeyboard);
	glutMouseFunc(myMouse);
	glutMainLoop();
}

運行結果



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章