// File: catmull-rom-spline.cxx // Written by: Michael Main (main@colorado.edu) on Sep 28, 2011 // he purpose of this program is to show how to draw a sequence // of catmull-rom parametric curves with four control points. //------------------------------------------------------------- // Directives: #include // In case I want to debug with cout #include // Provides EXIT_SUCCESS #include // All the graphics functions using namespace std; //------------------------------------------------------------- //------------------------------------------------------------- // Global Named Constants const int S = 500; // Size of a square graphics window const double W = 200; // Maximum world coordinate //------------------------------------------------------------- //------------------------------------------------------------- // Function Prototypes. // This function draws a parametric cubic curve on a square // graphics window of size pmax. The coordinate system for the // curve goes from -wmax to +wmax, with the origin in the // center of the screen. The curve goes from (x1,y1) to (x2,y2). // The direction coming out of (x1,y1) is the average of the // direction from the (x0,y0) and the direction to (x2,y2). // Similarly, the direction going into (x2,y2) is the average // of the directions from (x1,y1) and the direction to (x3,y3). void catmull_rom( int pmax, double wmax, double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3 ); // This function draws a filled circle in a square graphics // window of size pmax. The coordinate system for the circle // goes from -wmax to +wmax, with the origin at the center of // the screen. The center of the circle is specified in this // coordinate system as wx, wy, and its radius in pixels is // given by pixel_size. The fill color is given by the c // parameter. void fill_circle( int pmax, double wmax, double wx, double wy, int pixel_size, int c ); // This function assumes that a square graphics window of size // pmax is open. It waits for a mouse click and then converts // the coordinates of that click into a point in a coordinate // system where x can range from vx0 to vx1 and y can range // from vy0 to vy1). These new coordinates are returned in the // reference parameters wx and wy. void get_mouse( int pmax, double vx0, double vy0, double vx1, double vy1, double& x, double& y ); // pixel(v, v0, v1, p0, p1) converts v from an interval that // ranges from v0 to v1 into an integer interval that ranges // from p0 to p1. int pixel(double v, double v0, double v1, int p0, int p1); // world(p, v0, v1, p0, p1) is a coordinate transformation, // similar to the pixel function, but it goes in the other // direction (converting a pixel coordinate p (ranging // from p0 to p1) into a world coordinate (ranging // from v0 to v1). double world(int p, double v0, double v1, int p0, int p1); //------------------------------------------------------------- //------------------------------------------------------------- int main() { // This program always keeps track of four control points: double x0, y0, x1, y1, x2, y2, x3, y3; // Open the graphics window: initwindow(S, S, "Catmull-Rom Spline"); // Get three initial mouse clicks: get_mouse(S, -W, W, W, -W, x0, y0); fill_circle(S, W, x0, y0, 3, YELLOW); get_mouse(S, -W, W, W, -W, x1, y1); fill_circle(S, W, x1, y1, 3, YELLOW); get_mouse(S, -W, W, W, -W, x2, y2); fill_circle(S, W, x2, y2, 3, YELLOW); // Repeatedly get a 4th point and draw the next section. while (true) { // Get fourth point and draw the curve: get_mouse(S, -W, W, W, -W, x3, y3); fill_circle(S, W, x3, y3, 3, YELLOW); catmull_rom(S, W, x0, y0, x1, y1, x2, y2, x3, y3); // Shift points down to make room for a new 4th point: x0 = x1; y0 = y1; x1 = x2; y1 = y2; x2 = x3; y2 = y3; } return EXIT_SUCCESS; } //------------------------------------------------------------- //------------------------------------------------------------- // Note: Algorithm from Chapter 13 of Computer Graphics: // Programming in OpenGL for Visual Communication // by Steve Cunningham void catmull_rom( int pmax, double wmax, double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3 ) { double t; // "Time" parameter that controls where we are double t2, t3; // Powers of t double c0, c1, c2, c3; // Coef. for the cubic x-polynomial double d0, d1, d2, d3; // Coef. for the cubic y-polynomial double wx, wy; // World coordinates of next point int px, py; // Pixel coordinates of next point // Compute the coefficients for the cubic polynomials: c0 = x1; c1 = -0.5*x0 + 0.5*x2; c2 = x0 - 2.5*x1 + 2.0*x2 - 0.5*x3; c3 = -0.5*x0 + 1.5*x1 - 1.5*x2 + 0.5*x3; d0 = y1; d1 = -0.5*y0 + 0.5*y2; d2 = y0 - 2.5*y1 + 2.0*y2 - 0.5*y3; d3 = -0.5*y0 + 1.5*y1 - 1.5*y2 + 0.5*y3; // Move the drawing pen to the start of the curve: moveto( pixel(x1, -wmax, +wmax, 0, pmax), pixel(y1, +wmax, -wmax, 0, pmax) ); // For each possible t value draw a line to the point // of the curve that is determined by t: for (t = 0; t <= 1; t += 0.01) { // Compute the world coordinates of the t point: t2 = t*t; t3 = t*t2; wx = c0 + c1*t + c2*t2 + c3*t3; wy = d0 + d1*t + d2*t2 + d3*t3; // Convert to pixel coordinates and draw a line: px = pixel(wx, -wmax, +wmax, 0, pmax); py = pixel(wy, +wmax, -wmax, 0, pmax); lineto(px, py); } } //------------------------------------------------------------- //------------------------------------------------------------- void fill_circle( int pmax, double wmax, double wx, double wy, int pixel_size, int c ) { int px, py; // Pixel coordinates of the next point on curve px = pixel(wx, -wmax, +wmax, 0, pmax); py = pixel(wy, +wmax, -wmax, 0, pmax); setfillstyle(SOLID_FILL, c); fillellipse(px, py, pixel_size, pixel_size); } //------------------------------------------------------------- //------------------------------------------------------------- void get_mouse( int pmax, double vx0, double vy0, double vx1, double vy1, double& x, double& y ) { int px, py; // Pixel coordinates of a mouse click while (!ismouseclick(WM_LBUTTONDOWN)) { delay(100); } getmouseclick(WM_LBUTTONDOWN, px, py); x = world(px, vx0, vx1, 0, pmax); y = world(py, vy0, vy1, 0, pmax); } //------------------------------------------------------------- //------------------------------------------------------------- int pixel(double v, double v0, double v1, int p0, int p1) { return int(p0 + (v - v0)/(v1 - v0) * (p1 - p0)); } //------------------------------------------------------------- //------------------------------------------------------------- double world(int p, double v0, double v1, int p0, int p1) { double fraction = double(p - p0)/double(p1 - p0); double distance_from_v0 = fraction*(v1 - v0); return v0 + distance_from_v0; } //-------------------------------------------------------------