代码
#include <GLFW/glfw3.h>
#include<cmath>
#include<vector>
#include<iostream>
using namespace std;
#define WIDTH 900.f
#define HEIGHT 600.f
struct Color {
double r, g, b;
Color() { }
Color(double x, double y, double z)
:r(x), g(y), b(z) { }
} YELLOW(1, 1, 0), RED(1, 0, 0), BLACK(0.3, 0.3, 0.3);
struct Point {
double x, y;
Point() { }
Point(double a, double b)
:x(a), y(b) { }
Point operator* (const double &t) {
return Point(t*this->x, t*this->y);
}
Point operator+ (const Point &p) {
return Point(this->x + p.x, this->y + p.y);
}
};
vector<Point> points, moving_points, bspline_points;
vector<Point>::iterator moveIter, insertIter = points.begin();
vector<double> T;
int knotV = 2;
int k = 3;
bool DEL = false, MOVE = false, CLS = false, INS = false;
bool close_to(double x1, double y1, double x2, double y2) {
double dis = sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2));
if (dis <= 10) return true;
return false;
}
void drawControls(Color c) {
int beginMode[] = { GL_POINTS, GL_LINE_STRIP, GL_LINE_LOOP };
for (int mode = 0; mode < 2; mode++) {
if (mode == 0) glPointSize(9);
if (mode==1 && CLS) mode = 2;
glBegin(beginMode[mode]);
glColor3f(c.r, c.g, c.b);
for (auto iter = points.begin(); iter != points.end(); iter++) {
double xpos, ypos;
if (MOVE && !moving_points.empty() && iter == moveIter) {
xpos = moving_points[moving_points.size() - 1].x;
ypos = moving_points[moving_points.size() - 1].y;
}
else xpos = iter->x, ypos = iter->y;
xpos = (xpos - WIDTH / 2) / WIDTH * 2;
ypos = (HEIGHT / 2 - ypos) / HEIGHT * 2;
glVertex2f(xpos, ypos);
}
glEnd();
}
}
void drawCurve(Color c) {
glPointSize(2);
glBegin(GL_POINTS);
glColor3f(c.r, c.g, c.b);
for (auto p : bspline_points) {
double x = (p.x - WIDTH / 2) / WIDTH * 2;
double y = (HEIGHT / 2 - p.y) / HEIGHT * 2;
glVertex2f(x, y);
}
glEnd();
}
inline void uniform_KnotVector(int n) {
T.clear();
for (int i = 0; i <= n + k; i++)
T.push_back(i);
}
inline void openUniform_KnotVector(int n) {
T.clear();
double bgn = 0;
T.insert(T.begin(), k, bgn);
for (bgn = 1; bgn <= n + 1 - k; bgn++)
T.push_back(bgn);
T.insert(T.end(), k, bgn);
}
inline void bezier_KnotVector(int n) {
T.clear();
double bgn = 0;
T.insert(T.begin(), k, bgn);
bgn++;
for (int i = 1; i <= n + 1 - k; ) {
if (k > 1) {
T.insert(T.end(), k - 1, bgn++);
i += k - 1;
}
else {
T.push_back(bgn++);
i++;
}
}
T.insert(T.end(), k, bgn);
}
void deBoor(int j, double t, vector<Point> wrapPoints) {
vector<Point> deBo = wrapPoints;
for (int r = 1; r <= k - 1; r++) {
for (int i = j; i >= j - k + r + 1; i--) {
double n1 = (t - T[i]) / (T[i + k - r] - T[i]);
double n2 = (T[i + k - r] - t) / (T[i + k - r] - T[i]);
deBo[i] = (deBo[i] * n1) + (deBo[i - 1] * n2);
}
}
bspline_points.push_back(deBo[j]);
}
void bSpline() {
int n = points.size() - 1;
if (k < 1) return;
bspline_points.clear();
int p = k;
vector<Point> wrapPoints = points;
if (knotV == 1) uniform_KnotVector(n);
else if (knotV == 2) openUniform_KnotVector(n);
else if(knotV == 3) bezier_KnotVector(n);
else {
int p = k + 1;
if (n < k) p = n;
wrapPoints.insert(wrapPoints.end(), wrapPoints.begin(), wrapPoints.begin() + p);
n = wrapPoints.size() - 1;
uniform_KnotVector(n);
}
for (int j = k - 1; j <= n; j++)
for (double t = T[j]; t < T[j + 1]; t += 0.001)
deBoor(j, t, wrapPoints);
}
void leftButtonOp(double x, double y) {
for (auto iter = points.begin(); !DEL && !MOVE&&iter != points.end(); iter++) {
if (close_to(iter->x, iter->y, x, y)) {
MOVE = true;
moveIter = iter;
break;
}
}
if (MOVE)
moving_points.emplace_back(x, y);
}
void cursorPosCallback(GLFWwindow *window, double x, double y) {
if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == 1) {
leftButtonOp(x, y);
}
}
void mouseButtonCallback(GLFWwindow *window, int button, int action, int mods) {
double xpos, ypos;
glfwGetCursorPos(window, &xpos, &ypos);
if (DEL && button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) {
for (auto iter = points.begin(); iter != points.end(); iter++) {
if (close_to(iter->x, iter->y, xpos, ypos)) {
points.erase(iter);
break;
}
}
bSpline();
}
else if (button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_RELEASE) {
if (MOVE) {
moveIter->x = xpos;
moveIter->y = ypos;
}
else if (!MOVE && !DEL) {
points.emplace_back(xpos, ypos);
}
MOVE = false;
moving_points.clear();
bSpline();
}
else if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS) {
for (insertIter = points.begin(); insertIter != points.end(); insertIter++) {
if (close_to(xpos, ypos, insertIter->x, insertIter->y)) {
INS = true;
break;
}
}
}
else if (INS && button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_RELEASE) {
points.insert(insertIter, Point(xpos, ypos));
INS = false;
bSpline();
}
}
void keyCallback(GLFWwindow *window, int key, int sancode, int action, int mods) {
if (key == GLFW_KEY_DELETE && action == GLFW_PRESS) {
DEL = true;
}
else if (key == GLFW_KEY_DELETE && action == GLFW_RELEASE) {
DEL = false;
}
else if (key == GLFW_KEY_ENTER && action == GLFW_PRESS) {
points.clear();
moving_points.clear();
MOVE = DEL = false;
}
else if (key == GLFW_KEY_SPACE && action == GLFW_PRESS) {
points.clear();
moving_points.clear();
bspline_points.clear();
MOVE = DEL = false;
}
else if (key == GLFW_KEY_INSERT && action == GLFW_PRESS) {
cout << "input k: ";
cin >> k;
bSpline();
}
else if (mods == GLFW_MOD_CONTROL && action == GLFW_PRESS) {
switch (key)
{
case GLFW_KEY_1:
CLS = false;
knotV = 1;
bSpline();
break;
case GLFW_KEY_2:
CLS = false;
knotV = 2;
bSpline();
break;
case GLFW_KEY_3:
CLS = false;
knotV = 3;
bSpline();
break;
case GLFW_KEY_4:
CLS = true;
knotV = 4;
bSpline();
break;
default:
break;
}
}
else if (action == GLFW_PRESS) {
switch (key)
{
case GLFW_KEY_1:
k = 1;
bSpline();
break;
case GLFW_KEY_2:
k = 2;
bSpline();
break;
case GLFW_KEY_3:
k = 3;
bSpline();
break;
case GLFW_KEY_4:
k = 4;
bSpline();
break;
case GLFW_KEY_5:
k = 5;
bSpline();
break;
case GLFW_KEY_6:
k = 6;
bSpline();
break;
case GLFW_KEY_7:
k = 7;
bSpline();
break;
case GLFW_KEY_8:
k = 8;
bSpline();
break;
case GLFW_KEY_9:
k = 9;
bSpline();
break;
default:
break;
}
}
}
int main(void)
{
GLFWwindow* window;
if (!glfwInit())
return -1;
window = glfwCreateWindow(WIDTH, HEIGHT, "B-Spline", NULL, NULL);
glfwSetWindowPos(window, 600, 200);
if (!window)
{
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
while (!glfwWindowShouldClose(window))
{
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glfwSetMouseButtonCallback(window, mouseButtonCallback);
glfwSetCursorPosCallback(window, cursorPosCallback);
glfwSetKeyCallback(window, keyCallback);
drawControls(YELLOW);
if (!MOVE) {
drawCurve(RED);
}
glfwSwapBuffers(window);
glfwPollEvents();
}
glfwTerminate();
return 0;
}