/* * Example program for the Allegro library, by Shawn Hargreaves. * * This program demonstrates the use of spline curves to create smooth * paths connecting a number of node points. This can be useful for * constructing realistic motion and animations. * * The technique is to connect the series of guide points p1..p(n) with * spline curves from p1-p2, p2-p3, etc. Each spline must pass though * both of its guide points, so they must be used as the first and fourth * of the spline control points. The fun bit is coming up with sensible * values for the second and third spline control points, such that the * spline segments will have equal gradients where they meet. I came * up with the following solution: * * For each guide point p(n), calculate the desired tangent to the curve * at that point. I took this to be the vector p(n-1) -> p(n+1), which * can easily be calculated with the inverse tangent function, and gives * decent looking results. One implication of this is that two dummy * guide points are needed at each end of the curve, which are used in * the tangent calculations but not connected to the set of splines. * * Having got these tangents, it becomes fairly easy to calculate the * spline control points. For a spline between guide points p(a) and * p(b), the second control point should lie along the positive tangent * from p(a), and the third control point should lie along the negative * tangent from p(b). How far they are placed along these tangents * controls the shape of the curve: I found that applying a 'curviness' * scaling factor to the distance between p(a) and p(b) works well. * * One thing to note about splines is that the generated points are * not all equidistant. Instead they tend to bunch up nearer to the * ends of the spline, which means you will need to apply some fudges * to get an object to move at a constant speed. On the other hand, * in situations where the curve has a noticable change of direction * at each guide point, the effect can be quite nice because it makes * the object slow down for the curve. */ #include #include typedef struct NODE { int x, y; fixed tangent; } NODE; #define MAX_NODES 1024 NODE nodes[MAX_NODES]; int node_count; fixed curviness; int show_tangents; int show_control_points; /* calculates the distance between two nodes */ fixed node_dist(NODE n1, NODE n2) { #define SCALE 64 fixed dx = itofix(n1.x - n2.x) / SCALE; fixed dy = itofix(n1.y - n2.y) / SCALE; return fixsqrt(fixmul(dx, dx) + fixmul(dy, dy)) * SCALE; } /* constructs nodes to go at the ends of the list, for tangent calculations */ NODE dummy_node(NODE node, NODE prev) { NODE n; n.x = node.x - (prev.x - node.x) / 8; n.y = node.y - (prev.y - node.y) / 8; n.tangent = itofix(0); return n; } /* calculates a set of node tangents */ void calc_tangents(void) { int i; nodes[0] = dummy_node(nodes[1], nodes[2]); nodes[node_count] = dummy_node(nodes[node_count-1], nodes[node_count-2]); node_count++; for (i=1; i MAX_POINTS) npoints = MAX_POINTS; get_control_points(nodes[n], nodes[n+1], points); calc_spline(points, npoints, x, y); for (i=1; i> 8; if (c == KEY_ESC) break; else if (c == KEY_UP) { curviness += ftofix(0.05); draw_splines(); } else if (c == KEY_DOWN) { curviness -= ftofix(0.05); draw_splines(); } else if (c == KEY_SPACE) { walk(); draw_splines(); } else if (c == KEY_T) { show_tangents = !show_tangents; draw_splines(); } else if (c == KEY_C) { show_control_points = !show_control_points; draw_splines(); } } } return 0; } END_OF_MAIN()