計算機圖形學E11——B樣條曲線

其他計算機圖形學實驗見 鏈接

#include<gl/glut.h>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const float window_width = 800, window_height = 600;
const int d = 8;
const int maxn = 99999;

struct point
{
	float x, y;
	point() {}
	point(float xx, float yy)
		:x(xx), y(yy) {}
};
vector<point> input_vertice;
vector<point> control_point;

char OP;
int k = 3;
float controlpoint_color[3] = { 1,0,0 };
float straightline_color[3] = { 0,1,0 };
float Bspline_color[3] = { 0,0,1 };

float N[5];


void draw_a_point(float x, float y, float color[]);
void deBoor();
void mymouse(int button, int state, int x, int y);
void dragmouse(int x, int y);
void keyboard(unsigned char key, int x, int y);
int getdis(int x, int y);//獲取離得最近的點


int main(int argc, char* argv[])
{
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowPosition(50, 50);
	glutInitWindowSize(window_width, window_height);
	glutCreateWindow("de Boor 繪製B樣條曲線");
	cout << "點擊鍵盤數字2-4切換階數" << endl;
	cout << "不做操作則默認k=3,如需輸入其他階數,請按k後輸入階數" << endl;
	cout << "鍵盤點擊p後,點擊鼠標左鍵繪製控制點" << endl;
	cout << "鍵盤點擊i後,插入控制點" << endl;
	cout << "鍵盤點擊d後,移動控制點" << endl;
	cout << "鍵盤點擊e後,點擊鼠標左鍵刪除控制點" << endl;
	cout << "按ESC退出" << endl << endl;

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0, window_width, 0, window_height);

	glClearColor(1, 1, 1, 1);
	glClear(GL_COLOR_BUFFER_BIT);

	glutMouseFunc(&mymouse);
	glutMotionFunc(&dragmouse);
	glutKeyboardFunc(&keyboard);

	glutMainLoop();
	return 0;
}

void draw_a_point(float x, float y, float color[])
{
	glPointSize(3.0f);
	glBegin(GL_POINTS);
	glColor3fv(color);
	glVertex2f(x, y);
	glEnd();
	glFlush();
}

int getdis(int x, int y)
{
	int ans = -1;
	float shortdis = 99999999;
	for (int i = 0; i < input_vertice.size(); i++)
	{
		float dis = sqrt(pow(x - input_vertice[i].x, 2) + pow(y - input_vertice[i].y, 2));
		if (dis < shortdis && dis <= d)
		{
			shortdis = dis;
			ans = i;
		}
	}
	return ans;
}

int index1 = -1;
void mymouse(int button, int state, int x, int y)
{
	if (OP == 'p')//繪製控制點
	{
		if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
		{
			int index = getdis(x, window_height - y);
			if (index == -1)//鼠標範圍內沒有控制點,新加操作點
			{
				draw_a_point(x, window_height - y, controlpoint_color);
				point p(x, window_height - y);
				input_vertice.push_back(p);
				cout << "新輸入的控制點" << input_vertice.size() << ":(" << x << ", " << window_height - y << ")" << endl;
			}

			/*glBegin(GL_LINE_STRIP);
			glColor3fv(straightline_color);
			for (int i = 0; i < input_vertice.size(); i++)
				glVertex2f(input_vertice[i].x, input_vertice[i].y);
			glEnd();
			glFlush();*/
			control_point = input_vertice;
			deBoor();
		}
		
	}
	if (OP == 'i')//插入控制點
	{
		if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)
		{
			index1 = getdis(x, window_height - y);
			cout << "將插入新的控制點到 選中的控制點" << index1 << "前" << endl;
		}
		if (button == GLUT_LEFT_BUTTON && state == GLUT_UP && index1 != -1)
		{
			input_vertice.insert(input_vertice.begin() + index1, point(x, window_height - y));//在下標pos前插入一個元素
			//deBoor();
		}
		control_point = input_vertice;
		deBoor();
	}
	if (OP == 'e')
	{
		if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN)//刪除控制點
		{
			int index = getdis(x, window_height - y);
			cout << "刪除點" << index << " ";
			if (index != -1)//鼠標範圍內有控制點了,可以刪除,否則不做操作
			{
				vector<point>::iterator it = input_vertice.begin();
				cout << (*(it + index)).x << " " << (*(it + index)).y << endl;
				input_vertice.erase(it + index);
			}
			else
				cout << "沒有可以刪除的點" << endl;

			control_point = input_vertice;
			deBoor();
		}
	}
}

void dragmouse(int x, int y)
{
	int index = getdis(x, window_height - y);
	if (OP == 'd' && index != -1)//鼠標範圍內有控制點了,可以拖動
	{
		//cout << "修改控制點" << input_vertice.size() << ":(" << input_vertice[index].x << ", " << input_vertice[index].y << ")" << endl;
		input_vertice[index].x = x;
		input_vertice[index].y = window_height - y;

		control_point = input_vertice;

		//deBoor();
		glClear(GL_COLOR_BUFFER_BIT);
		//繪製控制點
		for (int i = 0; i < input_vertice.size(); i++)
			draw_a_point(input_vertice[i].x, input_vertice[i].y, controlpoint_color);
		//繪製多邊形
		glLineWidth(2.0f);
		glBegin(GL_LINE_STRIP);
		glColor3fv(straightline_color);
		for (int i = 0; i < input_vertice.size(); i++)
			glVertex2f(input_vertice[i].x, input_vertice[i].y);
		glEnd();
		glFlush();

		deBoor();
	}
}

void keyboard(unsigned char key, int x, int y)
{
	if (key == 27)
		exit(0);
	if (key == 'p')//畫點
	{
		OP = 'p';
		cout << "當前階數爲" << k << endl;
		cout << "請點擊鼠標左鍵,開始繪製控制點以及B樣條曲線" << endl;
	}
	if (key == 'd')//拖動端點
	{
		OP = 'd';
		cout << "請按住端點拖動" << endl;
	}
	if (key == 'i')//插入控制點
	{
		OP = 'i';
		cout << "請點擊一個已有的控制點按住不放,鬆開位置爲新插入頂點的位置。新插入的頂點位於點擊的頂點之前。 " << endl;
	}
	if (key == 'e')
	{
		OP = 'e';
		cout << "鼠標左鍵點擊要刪除的點" << endl;
	}
	if (key == '2')
	{
		k = 2;
		cout << "修改階數爲2,重新繪製B樣條曲線" << endl;
		deBoor();
	}
	if (key == '3')
	{
		k = 3;
		cout << "修改階數爲3,重新繪製B樣條曲線" << endl;
		deBoor();
	}
	if (key == '4')
	{
		k = 4;
		cout << "修改階數爲4,重新繪製B樣條曲線" << endl;
		deBoor();
	}
	if (key == 'k')
	{
		cout << "修改階數爲:" << endl;
		cin >> k;
		cout << "重新繪製B樣條曲線" << endl;
		deBoor();
	}
}

void deBoor()
{
	vector<point> b_spline;
	int n = control_point.size() - 1;
	//cout <<"control_point_size-1=" << n << endl;

	float t[maxn];
	//均勻B樣條
	/*for (int i = 0; i <= input_vertice.size() + k; i++)
		t[i] = i + 1;*/
	//準均勻B樣條
	t[0] = 0; 
	for (int i = 0; i <= k - 1; i++)
		t[i] = 0;
	for (int i = k; i < n + 1; i++)
		t[i] = t[i - 1] + 1.0 / (n + 1 - k + 1);
	for (int i = n + 1; i <= n + k; i++)
		t[i] = 1;
	
	for (int j = k - 1; j <= n; j++)//j爲區間的末端點
	{
		for (double u = t[j]; u <= t[j + 1]; u += 0.001 / n)
		{
			for (int r = 1; r <= k - 1; r++)//遞推k-1層
			{
				for (int i = j; i >= j - k + r + 1; i--)//由遞推公式得,需要倒着來,i和i-1存在i中;;bezier爲j和j+1存在j中
				{
					float x1 = u - t[i];
					float x2 = t[i + k - r] - t[i];
					float y1 = t[i + k - r] - u;
					float y2 = t[i + k - r] - t[i];

					float coefficient1, coefficient2;
					if (x1 == 0.0 && x2 == 0.0)
						coefficient1 = 0;
					else
						coefficient1 = x1 / x2;
					if (y1 == 0.0 && y2 == 0.0)
						coefficient2 = 0;
					else
						coefficient2 = y1 / y2;

					if (r == 1)//第一輪必須是輸入的那幾個控制點
					{
						control_point[i].x = input_vertice[i].x * coefficient1 + input_vertice[i - 1].x * coefficient2;
						control_point[i].y = input_vertice[i].y * coefficient1 + input_vertice[i - 1].y * coefficient2;
						continue;
					}
					else
					{
						control_point[i].x = control_point[i].x * coefficient1 + control_point[i - 1].x * coefficient2;
						control_point[i].y = control_point[i].y * coefficient1 + control_point[i - 1].y * coefficient2;
					}
				}
			}
			b_spline.push_back(control_point[j]);//遞推的最後一層的點,即爲求得的點
		}
	}
	//cout << "BSpline size = :" << b_spline.size() << endl;

	glClear(GL_COLOR_BUFFER_BIT);//清空屏幕上上一次的曲線
	//繪製控制點
	for (int i = 0; i < input_vertice.size(); i++)
		draw_a_point(input_vertice[i].x, input_vertice[i].y, controlpoint_color);
	//繪製控制多邊形
	glBegin(GL_LINE_STRIP);
	glColor3fv(straightline_color);
	for (int i = 0; i < input_vertice.size(); i++)
		glVertex2f(input_vertice[i].x, input_vertice[i].y);
	glEnd();
	glFlush();
	//畫出b_spline曲線
	glLineWidth(3.0f);
	glBegin(GL_LINE_STRIP);
	glColor3fv(Bspline_color);
	for (int i = 0; i < b_spline.size(); i++)
		glVertex2f(b_spline[i].x, b_spline[i].y);
	glEnd();
	glFlush();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章