You are on page 1of 24

��// B-Spline.

cpp

//////////////////////////////////////////////////////////////////////////////

// Note:

//

// This code is for demo purpose only.

// There is no warranty that my code is totally bug-free or no-harm at all,

// anyone likes to copy or try it has to take the risk by her or him-self.

//

// Copyright is reserved.

//

//

// Author comment:

// The major purpose of this program is used to demo how to convert any NURBS

// curve into poly-Bezier segments via the algorithm of my "Knots Equations".

//

// Once you finished a NURBS drawing with this program, you can verify it with

// OpenGL command "gluNurbsCurve()" by press the 'V' key.

//

//

// Author: Hunt Chang 20150524

//////////////////////////////////////////////////////////////////////////////

#include <direct.h>

#include <stdlib.h>

#include <stdio.h>

#include <math.h>

#include <vector>

#include <crtdbg.h>
#include <freeglut.h>

// key function

//

// 2 Bez2 B-Spline curve

// 3 Bez3 B-Spline curve

// 4 Bez4 B-Spline curve

// c/C clear Control Points

// u/U Toggle Uniform or Non-Uniform B-Spline mode

// f/F Toggle clamped mode on first-end

// l/L Toggle clamped mode on last-end

// b/B if B-Spline is in closed loop then break it at its original ends

// k/K reset knots array

// h/H show BezPts[] of all Bez3 segments in Non-Uniform mode

// v/V if B-Spline is in Non-Uniform mode then verify it by a double draw of


B-Spline with OpenGL command

// s/S saveData(): CtrlPts/RingPts, OpenNuts/RingNuts & BezPts to


<dataLog.txt> file.

// ESC quit

//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////

class BitAry

std::vector<unsigned int> bits;

int _max;

public:
BitAry(int m = 0) { bits.resize(m/32+1); _max = m; }

void SetBit(int k)

_ASSERTE(k <= _max);

bits[k/32] |= (1 << (k%32));

void ClearBit(int k)

_ASSERTE(k <= _max);

bits[k/32] &= ~(1 << (k%32));

bool ChkBit(int k)

_ASSERTE(k <= _max);

unsigned int kk = k%32, bit = (bits[k/32] & (1 << kk));

return (1 == bit >> kk);

void SetAll()

_ASSERTE(_max > 0);

int n = bits.size();

memset(&bits[0], 0xFF, n*sizeof(int));

void Resize(int m)

bits.clear(); bits.resize(m/32+1);

_max = m;

};
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////

const double FLT_EPSILON = 1.192092896e-07F;

struct Vec2D

double x, y;

Vec2D(double dx = 0.0, double dy = 0.0) { x = dx; y = dy; }

Vec2D operator-() { return Vec2D(-x, -y); }

Vec2D operator+(Vec2D pt) { return Vec2D(x+pt.x, y+pt.y); }

Vec2D operator-(Vec2D pt) { return Vec2D(x-pt.x, y-pt.y); }

Vec2D operator*(double d) { return Vec2D(x*d, y*d); }

Vec2D operator/(double d) { return Vec2D(x/d, y/d); }

bool operator==(Vec2D pt) { return (FLT_EPSILON >= abs(x-pt.x) &&


FLT_EPSILON >= abs(y-pt.y)); }

bool operator!=(Vec2D pt) { return (FLT_EPSILON < abs(x-pt.x) ||


FLT_EPSILON < abs(y-pt.y)); }

};

//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////

#define NoteMsg(msg) ::MessageBox(::GetActiveWindow(), msg, "Notice:",


MB_ICONINFORMATION)

enum BSpl_Const { QUAD = 2, CUBIC = 3, QUART = 4, };


#define SEG 64

#define KEY_ESC 27

// window size

int ww = 800;

int wh = 640;

int NDEG;

int pickRadius = 5;

int lIdx = -1; // used to remember the moving control index

bool bUnif = true; // set to use uniform B-Spline

bool bRing = false; // B-Spline is in Ring mode

bool bClampF = true; // set clamp at first end

bool bClampL = true; // set clamp at Last end

bool bVerify = false; // draw a verification curve by gluNurbsCurve()


command

bool bHull = false; // show poly-Bezier segments' control hull

char txtBuf[64];

char title[] = "NURBS";

char deg2[] = "Quadratic", deg3[] = "Cubic", deg4[] = "Quartic";

char typ0[] = "Uniform ", typ1[] = "Non-uniform ";

std::vector<Vec2D> CtrlPts; // core record of NURBS' control points

std::vector<Vec2D> RingPts; // RingPts[], control-points used by Ring-NURBS.

std::vector<double> coreNuts; // coreNuts is the base of Knots[]

std::vector<double> OpenNuts; // knots array used by Open-NURBS

std::vector<double> RingNuts; // knots array used by Ring-NURBS.

std::vector<Vec2D> BezPts; // this is the polyBez array used to draw NURBS.

std::vector<Vec2D> tFL(2); // records the cF(Cubic) or dF(Quartic) and bL of


polyBez.

BitAry Bits; // records the validity of each curve-segment.

GLUnurbsObj *pNurb = NULL;

/////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////

void clearData()

bRing = false; CtrlPts.clear(); coreNuts.clear();

void drawCtrlPoly(int n, Vec2D pt[])

glLineWidth(1.0); glColor3f(0.85f, 0.85f, 0.98f); // silver

glPushAttrib(GL_ENABLE_BIT);

glLineStipple(1, 0x00FF);

glEnable(GL_LINE_STIPPLE);

glBegin(GL_LINE_STRIP);

for (int i=0; i<n; i++) glVertex2d(pt[i].x, pt[i].y);

glEnd();

glDisable(GL_LINE_STIPPLE);

glPopAttrib();

void drawPts(int n, Vec2D pt[])

glBegin(GL_POINTS);
for (int i=0; i<n; i++) glVertex2d(pt[i].x, pt[i].y);

glEnd();

void drawHullPts(int n, Vec2D pt[])

glBegin(GL_POINTS);

for (int i=0; i<n; i++) {

if (0 == i%NDEG) { glColor3f(0.66f,0.66f,0.66f); glPointSize(8.0);


} // gray

else { glColor3f(0.8f,0.5f,0.2f);
glPointSize(5.0); } // gold

glVertex2d(pt[i].x, pt[i].y);

glEnd();

void drawHullLine(int n, Vec2D pt[])

glPushAttrib(GL_ENABLE_BIT); glLineWidth(1.0);

glLineStipple(1, 0x0707); glEnable(GL_LINE_STIPPLE); // dashed


pattern

for (int j=0; j<n/NDEG; j++) {

if (! Bits.ChkBit(j)) continue;

glBegin(GL_LINE_STRIP);

for (int i=0; i<=NDEG; i++) glVertex2d(pt[j*NDEG+i].x,


pt[j*NDEG+i].y);

glEnd();

glDisable(GL_LINE_STIPPLE);

glPopAttrib();
}

void pickPt(int button, int state, int xPos, int yPos)

double dx, dy, newX = xPos, newY = wh - yPos; int n =


CtrlPts.size();

if (state == GLUT_DOWN) {

if (button == GLUT_LEFT_BUTTON) {

for (int i = 0; i < n; i++) {

dx = CtrlPts[i].x-newX; dy = CtrlPts[i].y-newY;

if (sqrt(dx*dx+dy*dy) <= pickRadius) {

lIdx = i; break;

if (state == GLUT_UP) {

if (button == GLUT_LEFT_BUTTON) {

if (-1 == lIdx) {

if (bRing) return;

CtrlPts.push_back(Vec2D(newX, newY));

} else if (-1 < lIdx) {

CtrlPts[lIdx].x = newX; CtrlPts[lIdx].y = newY;

if (bRing) {

if (0 == lIdx) CtrlPts[n-1] = CtrlPts[0];

else if (n-1 == lIdx) CtrlPts[0] = CtrlPts[n-1];

RingPts.clear();

lIdx = -1;

}
}

glutPostRedisplay();

void mouseMove(int xPos, int yPos)

int n = CtrlPts.size(), newX = xPos, newY = wh - yPos;


double dx, dy;

if (lIdx > -1) {

CtrlPts[lIdx].x = newX; CtrlPts[lIdx].y = newY;

if (n > 3) {

if (! bRing) {

if (0 == lIdx) {

dx = newX-CtrlPts[n-1].x; dy = newY-CtrlPts[n-1].y;

bRing = (sqrt(dx*dx+dy*dy) <= pickRadius) ? true : false;

if (bRing) { CtrlPts[n-1] = CtrlPts[0]; RingPts.clear();


}

} else if (n-1 == lIdx) {

dx = newX-CtrlPts[0].x; dy = newY-CtrlPts[0].y;

bRing = (sqrt(dx*dx+dy*dy) <= pickRadius) ? true : false;

if (bRing) { CtrlPts[0] = CtrlPts[n-1]; RingPts.clear();


}

} else {

if (0 == lIdx) CtrlPts[n-1] = CtrlPts[0];

if (n-1 == lIdx) CtrlPts[0] = CtrlPts[n-1];

RingPts.clear();

glutPostRedisplay();
}

void setKnots()

int n = CtrlPts.size(), ORD = NDEG+1, nNuts = n+ORD, m =


coreNuts.size();

if (m != nNuts) { RingPts.clear(); BezPts.clear(); }

coreNuts.resize(nNuts);

if (bUnif) {

for (int i=0; i<nNuts; i++) coreNuts[i] = i;

} else {

srand(GetTickCount());

if (0 == m) {

coreNuts[0] = 0;

for (int i=1; i<nNuts; i++) coreNuts[i] = coreNuts[i-1]+((double)


(rand() % 10));

} else if (m < nNuts) {

for (int i=m; i<nNuts; i++) coreNuts[i] = coreNuts[i-1]+((double)


(rand() % 10));

if (bRing) {

m = NDEG-1; nNuts += m; RingNuts.resize(nNuts);

int k = n+1; memcpy(&RingNuts[0], &coreNuts[0], sizeof(double)*k);

for (int i=k; i<nNuts; i++) RingNuts[i] = RingNuts[i-n+1]-RingNuts[i-


n]+RingNuts[i-1];

if (0 == RingPts.size()) {

k = n+m; RingPts.resize(k); memcpy(&RingPts[0], &CtrlPts[0],


sizeof(Vec2D)*n);

for (int i=n; i<k; i++) RingPts[i] = CtrlPts[i-n+1];

}
} else {

OpenNuts.resize(nNuts); memcpy(&OpenNuts[0], &coreNuts[0],


sizeof(double)*nNuts);

if (bClampF) { for (int i=NDEG; i>0; i--) OpenNuts[i-


1] = OpenNuts[i]; }

if (bClampL) { for (int i=n; i<(n+NDEG); i++)


OpenNuts[i+1] = OpenNuts[i]; }

void verifyGL(int nPt, Vec2D pt[], double nut[])

int ORD = NDEG+1, k = nPt+ORD;

std::vector<float> fPt(nPt*3); std::vector<float> fNut(k);

for(int j=0; j<nPt; ++j) { fPt[j*3] = (float) pt[j].x; fPt[j*3+1] =


(float) pt[j].y; fPt[j*3+2] = 0; }

for(int j=0; j<k; ++j) fNut[j] = (float) nut[j];

glLineWidth(1); glColor3ub(255,0,255); // purple B-Spline

pNurb = gluNewNurbsRenderer();

gluNurbsProperty(pNurb, GLU_SAMPLING_TOLERANCE, 15);

gluBeginCurve(pNurb);

gluNurbsCurve(pNurb, k, &fNut[0], 3, &fPt[0], ORD, GL_MAP1_VERTEX_3);

gluEndCurve(pNurb);

if (pNurb) gluDeleteNurbsRenderer(pNurb);

void drawBez2Cuv()

Vec2D A, B, pt; double u; int n = BezPts.size();

glLineWidth(1);

for (int j=0; j<n/QUAD; j++) {

if (! Bits.ChkBit(j)) continue;
B = BezPts[j*2+1]-BezPts[j*2]; A = BezPts[j*2+2]-
BezPts[j*2+1]*2+BezPts[j*2];

glBegin(GL_LINE_STRIP);

for (int k=0; k<=SEG; k++) {

u = ((double) k)/SEG; pt = (A*u+B*2)*u+BezPts[j*2];

glVertex2d(pt.x, pt.y);

glEnd();

void drawBez3Cuv()

Vec2D A, B, C, pt; double u; int n = BezPts.size();

glLineWidth(1);

for (int j=0; j<n/CUBIC; j++) {

if (! Bits.ChkBit(j)) continue;

A = BezPts[j*3+3]-BezPts[j*3]-(BezPts[j*3+2]-BezPts[j*3+1])*3;

C = BezPts[j*3+1]-BezPts[j*3]; B = BezPts[j*3+2]-
BezPts[j*3+1]*2+BezPts[j*3];

glBegin(GL_LINE_STRIP);

for (int k=0; k<=SEG; k++) {

u = ((double) k)/SEG; pt = (A*u*u+(B*u+C)*3)*u+BezPts[j*3];

glVertex2d(pt.x, pt.y);

glEnd();

void drawBez4Cuv()
{

Vec2D A, B, C, D, pt; double u, uu; int n = BezPts.size();

glLineWidth(1);

for (int j=0; j<n/QUART; j++) {

if (! Bits.ChkBit(j)) continue;

A = BezPts[j*4+4]+BezPts[j*4]+BezPts[j*4+2]*6-
(BezPts[j*4+3]+BezPts[j*4+1])*4;

B = BezPts[j*4+3]-BezPts[j*4]-(BezPts[j*4+2]-BezPts[j*4+1])*3;

D = BezPts[j*4+1]-BezPts[j*4]; C = BezPts[j*4+2]-
BezPts[j*4+1]*2+BezPts[j*4];

glBegin(GL_LINE_STRIP);

for (int k=0; k<=SEG; k++) {

u = ((double) k)/SEG; uu= u*u; pt = ((A*uu+C*6)*u+


(B*uu+D)*4)*u+BezPts[j*4];

glVertex2d(pt.x, pt.y);

glEnd();

void setPolyBez2(int n, double nut[], Vec2D pt[])

_ASSERTE(n > QUAD);

double d, t; int mx = n-2, m = mx*2+3, k = m-2;


std::vector<Vec2D> tv(m);

Bits.Resize(mx); Bits.SetAll(); tv[0] = pt[0]; tv[m-1] =


pt[n-1];

for (int i=0; i<mx; i++) {

tv[i*2+2] = pt[i+1]; d = nut[i+3]-nut[i+1];

if (0 != d) { t = (nut[i+2]-nut[i+1])/d; tv[i*2+1] = (pt[i+1]-


pt[i])*t+pt[i]; }

else {

Bits.ClearBit(i);
if (0 < i) Bits.ClearBit(i-1);

d = nut[mx+3]-nut[mx+1];

if (0 != d) { t = (nut[mx+2]-nut[mx+1])/d; tv[k] = (pt[n-1]-pt[n-


2])*t+pt[n-2]; }

else Bits.ClearBit(mx-1);

BezPts.resize(k); memcpy(&BezPts[0], &tv[1], sizeof(Vec2D)*k);

void setPolyBez3(int n, double nut[], Vec2D pt[])

_ASSERTE(n > CUBIC);

Vec2D cF, bL; int mx = n-3, m = mx*3+3, k = m-2;


std::vector<Vec2D> tv(m);

double t, d = nut[4]-nut[1]; Bits.Resize(mx); Bits.SetAll();

if (0 != d) { t = (nut[4]-nut[3])/d; tv[0] =
pt[0]*t+pt[1]*(1.0-t); }

for (int i=0; i<mx; i++) {

if (0 != (d = nut[i+5]-nut[i+2])) {

t = (nut[i+5]-nut[i+3])/d; tv[i*3+2] = pt[i+1]*t+pt[i+2]*(1.0-t);

t = (nut[i+5]-nut[i+4])/d; tv[i*3+3] = pt[i+1]*t+pt[i+2]*(1.0-t);

if (0 == (d = nut[i+4]-nut[i+2])) Bits.ClearBit(i);

else { t = (nut[i+3]-nut[i+2])/d; tv[i*3+1] =


tv[i*3+2]*t+tv[i*3]*(1.0-t); }

} else Bits.ClearBit(i);

if ((! Bits.ChkBit(i)) && (0 < i)) Bits.ClearBit(i-1);

if (0 != (d = nut[mx+5]-nut[mx+2])) {

t = (nut[mx+5]-nut[mx+3])/d; tv[m-1] = pt[n-2]*t+pt[n-1]*(1.0-t);

if (0 == (d = nut[mx+4]-nut[mx+2])) Bits.ClearBit(mx-1);
else { t = (nut[mx+3]-nut[mx+2])/d; tv[k] = tv[k+1]*t+tv[k-
1]*(1.0-t); }

} else Bits.ClearBit(mx-1);

BezPts.resize(k); memcpy(&BezPts[0], &tv[1], sizeof(Vec2D)*k);

tFL[0] = tv[0]; tFL[1] = tv[m-1];

void setPolyBez4(int n, double nut[], Vec2D pt[])

_ASSERTE(QUART < n || (3 < n && bRing));

Vec2D cF, bL, tp0, tp1; int k, mx = n-4, m = mx*4+3;

double d0, d1, d2, n0, n1, n2, t0, t1, t2, t3; std::vector<Vec2D>
tv(m);

Bits.Resize(mx); Bits.SetAll();

d0 = nut[5]-nut[1]; d1 = nut[6]-nut[2]; d2 = nut[5]-nut[2];

if (0 != d2) {

n0 = nut[5]-nut[4]; n1 = nut[4]-nut[2]; t0 = n0/d0; t1 =


n1/d1; t2 = n0/d2;

tv[0] = (pt[0]*t0+pt[1]*(1.0-t0))*t2+(pt[2]*t1+pt[1]*(1.0-t1))*(1.0-t2);

for (int i=0; i<mx; i++) {

d0 = nut[i+6]-nut[i+2]; d1 = nut[i+7]-nut[i+3]; d2 = nut[i+6]-


nut[i+3];

if (0 == d2) Bits.ClearBit(i);

else {

n0 = nut[i+4]-nut[i+3]; n1 = nut[i+6]-nut[i+4]; n2 = nut[i+5]-


nut[i+3];

t0 = n1/d0; t1 = n0/d1; t2 = n0/d2; t3 = n2/d1;

tp0 = pt[i+1]*t0+pt[i+2]*(1.0-t0); tp1 = pt[i+3]*t3+pt[i+2]*(1.0-t3);

tv[i*4+2] = tp0*(1.0-t2)+(pt[i+3]*t1+pt[i+2]*(1.0-t1))*t2;

n0 = nut[i+5]-nut[i+4]; t1 = n0/d2; t0 = (nut[i+5]-


nut[i+2])/d0;

tv[i*4+3] = tp0*(1.0-t1-t2)+tp1*t2+pt[i+2]*t1;
t2 = n2/d2; tv[i*4+4] = (pt[i+1]*(1.0-t0)+pt[i+2]*t0)*(1.0-
t2)+tp1*t2;

if (0 != n2) { t0 = n0/n2; tv[i*4+1] = tv[i*4]*t0+tv[i*4+2]*(1.0-


t0); }

else Bits.ClearBit(i);

if ((! Bits.ChkBit(i)) && (0 < i)) Bits.ClearBit(i-1);

d1 = nut[mx+7]-nut[mx+3]; d2 = nut[mx+6]-nut[mx+3]; k = m-2;

if (0 != d2) {

t1 = n0/d1; t2 = n0/d2; tv[m-1] = tp1*(1-t2)+(pt[n-1]*t1+pt[n-


2]*(1.0-t1))*t2;

if (0 != n1) { t1 = n0/n1; tv[k] = tv[k-1]*(1.0-


t1)+tv[k+1]*t1; }

else Bits.ClearBit(mx-1);

} else Bits.ClearBit(mx-1);

BezPts.resize(k); memcpy(&BezPts[0], &tv[1], sizeof(Vec2D)*k);

tFL[0] = tv[0]; tFL[1] = tv[m-1];

void saveData()

FILE *fp; char buf[8192], file[] = "\\dataLog.txt", nL[] =


"---------------- New Log ----------------\n", *p;

_getcwd(buf, _MAX_PATH);

_ASSERTE(NULL != buf);

strcat_s(buf, file);

int k, m, n, L, o; Vec2D *pt; double *dk;

n = ((bRing) ? RingPts.size() : CtrlPts.size());

if ((NDEG == QUAD && 3 > n) || (NDEG == CUBIC && 4 > n) || (NDEG == QUART && 5 >
n)) {

NoteMsg("Number of Control points are too few to draw a Curve-Segment. ");


return;
}

if (bRing) { pt = &RingPts[0]; dk = &RingNuts[0]; k =


RingNuts.size(); }

else { pt = &CtrlPts[0]; dk = &OpenNuts[0]; k = OpenNuts.size();


}

if (0 != (fopen_s(&fp, buf,"a+"))) return;

fwrite(nL, sizeof(nL), 1, fp);

if (NDEG == QUAD) p = deg2;

else if (NDEG == CUBIC) p = deg3;

else p = deg4;

m = sprintf_s(buf, 8192, "\t%s%s B-Spline; \tRing-mode: %s\n", ((bUnif) ? typ0 :


typ1), p, ((bRing) ? "ON" : "OFF"));

fwrite(buf, m, 1, fp);

// list control points

m = sprintf_s(buf, 8192, "Control Points: %d\n", n); L = 5; o = n/L;

for (int i=0; i<o; i++) {

for(int j=0; j<L; ++j) {

m += sprintf_s(buf+m, 8192-m, " (%.02f, %.02f); ", pt[i*L+j].x,


pt[i*L+j].y);

m += sprintf_s(buf+m, 8192-m, "\n");

o *= L; L = n%L;

if (0 < L) {

for(int j=0; j<L; ++j) {

m += sprintf_s(buf+m, 8192-m, " (%.02f, %.02f); ", pt[o+j].x, pt[o+j].y);

m += sprintf_s(buf+m, 8192-m, "\n");

fwrite(buf, m, 1, fp);

// list knots array


m = sprintf_s(buf, 8192, "Knots array: %d\n", k); L = 10; o = k/L;

for (int i=0; i<o; i++) {

m += sprintf_s(buf+m, 8192-m, " { ");

for(int j=0; j<L; ++j) {

m += sprintf_s(buf+m, 8192-m, "%.04f, ", dk[i*L+j]);

m += sprintf_s(buf+m, 8192-m, "}\n");

o *= L; L = k%L;

if (0 < L) {

m += sprintf_s(buf+m, 8192-m, " { ");

for(int j=0; j<L; ++j) {

m += sprintf_s(buf+m, 8192-m, "%.04f, ", dk[o+j]);

m += sprintf_s(buf+m, 8192-m, "}\n");

fwrite(buf, m, 1, fp);

// list BezPts & Bits redords

m = sprintf_s(buf, 8192, "Control Points of Poly-Bezier Curve:\n");

k = BezPts.size(); o = k/NDEG; pt = &BezPts[0];

for (int i=0; i<o; i++) {

m += sprintf_s(buf+m, 8192-m, " %s\t", ((Bits.ChkBit(i)) ? "VALID" :


"NULL"));

for(int j=0; j<NDEG; ++j) {

m += sprintf_s(buf+m, 8192-m, " (%.15f, %.15f); ", pt[i*NDEG+j].x,


pt[i*NDEG+j].y);

m += sprintf_s(buf+m, 8192-m, "\n");

m += sprintf_s(buf+m, 8192-m, "\t (%.15f, %.15f); \n\n", pt[k-1].x, pt[k-1].y);

fwrite(buf, m, 1, fp); fclose(fp); NoteMsg("Data have been saved.


");

void drawNURBS()

int n = CtrlPts.size(), m = n-4; Vec2D *pt; double *pNut;

if (0 == n) return;

glPointSize(4.0); glColor3f(1.0f, 0, 1.0f); // purple

drawPts(n, &CtrlPts[0]); drawCtrlPoly(n, &CtrlPts[0]);

if (bRing || (QUAD < n && NDEG == QUAD) || (CUBIC < n && NDEG == CUBIC) ||
(QUART < n && NDEG == QUART))

setKnots();

else return;

// set BezPts[]

if (bRing) { pt = &RingPts[0]; pNut = &RingNuts[0]; n =


RingPts.size(); }

else { pt = &CtrlPts[0]; pNut = &OpenNuts[0]; }

if (NDEG == QUAD) setPolyBez2(n, pNut, pt);

else if (NDEG == CUBIC) setPolyBez3(n, pNut, pt);

else if (NDEG == QUART) setPolyBez4(n, pNut, pt);

// draw B-Spline

if (bUnif) glColor3f(0,1.0f,0); // green uniform B-Spline

else glColor3f(0,0,1.0f); // blue non-uniform B-Spline

if (NDEG == QUAD) drawBez2Cuv();

else if (NDEG == CUBIC) drawBez3Cuv();

else if (NDEG == QUART) drawBez4Cuv();

// verify curve with OpenGL command

if (bVerify) {

verifyGL(n, pt, pNut); bVerify = false;

if (bHull) {
drawHullPts(BezPts.size(), &BezPts[0]);

if ((NDEG == CUBIC || NDEG == QUART) && (!bRing)) {

glColor3f(0,0,0); drawPts (2, &tFL[0]);

glColor3f(0,0.8f,0.8f); drawHullLine(BezPts.size(), &BezPts[0]); //


cyan

bHull = false;

void init()

NDEG = CUBIC; bUnif = bClampF = bClampL = false;

glClearColor(1.0, 1.0, 1.0, 0.0);

clearData();

void setTitle()

char *p;

int k = sprintf_s(txtBuf, 64, "%s: %s", title, ((bUnif) ? typ0 : typ1));

if (NDEG == QUAD) p = deg2;

else if (NDEG == CUBIC) p = deg3;

else p = deg4;

sprintf_s(txtBuf+k, 64-k, "%s mode", p);

void keyIn(unsigned char ch, int x, int y)

int oldMode = NDEG; bool bTxt = false;


switch(ch) {

case '2':

NDEG = QUAD;

if (oldMode == NDEG) return;

else { coreNuts.clear(); bTxt = true; }

break;

case '3':

NDEG = CUBIC;

if (oldMode == NDEG) return;

else { coreNuts.clear(); bTxt = true; }

break;

case '4':

NDEG = QUART;

if (oldMode == NDEG) return;

else { coreNuts.clear(); bTxt = true; }

break;

case 'u': case 'U':

bUnif = ! bUnif; RingPts.clear(); bTxt = true;

break;

case 'f': case 'F': bClampF = ! bClampF; break;

case 'l': case 'L': bClampL = ! bClampL; break;

case 'b': case 'B':

if (bRing) {

CtrlPts[0].x -= 15; CtrlPts[0].y += 15;

RingPts.clear(); bRing = false;

} else return;

break;

case 'k': case 'K':

if (bUnif) return;
else coreNuts.clear();

break;

case 'v': case 'V':

if ((QUAD == NDEG && 2 < CtrlPts.size()) || (CUBIC == NDEG && 3 <


CtrlPts.size()) || (QUART == NDEG && 4 < CtrlPts.size()))

bVerify = true;

else if (bRing && (QUART == NDEG && 3 < CtrlPts.size())) bVerify =


true;

else return;

break;

case 'h': case 'H':

if ((QUAD == NDEG && 2 < CtrlPts.size()) || (CUBIC == NDEG && 3 <


CtrlPts.size())) bHull = true;

else if (QUART == NDEG) {

if (bRing && 3 < CtrlPts.size()) bHull = true;

else if (4 < CtrlPts.size()) bHull = true;

else return;

break;

case 'c': case 'C': clearData(); break;

case 's': case 'S': saveData(); return;

case KEY_ESC: exit(EXIT_SUCCESS);

if (bTxt) { setTitle(); glutSetWindowTitle(txtBuf); }

glutPostRedisplay();

void display()

glClear(GL_COLOR_BUFFER_BIT);

drawNURBS();
glutSwapBuffers();

void reshape(int w, int h)

glViewport(0, 0, (GLsizei) w, (GLsizei) h);

ww = w; wh = h;

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

gluOrtho2D(0.0, w, 0.0, h);

void listCommand()

printf("\nkey function:\n\n");

printf("\tmouse-click\tadd new control point\n");

printf("\tmouse-drag \tmove control point\n");

printf("\t2\tdraw Quadratic B-Spline curve\n");

printf("\t3\tdraw Cubic B-Spline curve\n");

printf("\t4\tdraw Quartic B-Spline curve\n");

printf("\tu or U\ttoggle Uniform or Non-Uniform B-Spline mode\n");

printf("\tf or F\ttoggle clamped curve on first-end\n");

printf("\tl or L\ttoggle clamped curve on last-end\n");

printf("\tb or B\tbreak ring-curve mode\n");

printf("\tk or K\treset knot array (Non-Uniform mode only)\n");

printf("\th or H\tshow poly-Bezier control points\n");

printf("\tv or V\tverify NURBS curve by OpenGL-command <gluNurbsCurve()>\n");

printf("\tc or C\tclear all control point(s)\n");

printf("\ts or S\tsave data to file <dataLog.txt>\n");

printf("\tESC \tquit\n\n");
printf(" Author: Hunt Chang 20141130\n");

int main(int argc, char **argv)

glutInit(&argc, argv);

listCommand();

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);

glutInitWindowSize(ww, wh);

glutInitWindowPosition(400,50);

setTitle();

glutCreateWindow(txtBuf);

glutDisplayFunc(display);

glutMouseFunc(pickPt);

glutMotionFunc(mouseMove);

glutKeyboardFunc(keyIn);

glutReshapeFunc(reshape);

init();

glutMainLoop();

return 0;

/////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////

You might also like