代码
#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 ;
}