4 * An OpenGL based 'interactive canvas' library.
6 * Authored By Tomas Frydrych <tf@openedhand.com>
8 * Copyright (C) 2007 OpenedHand
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
26 #include "clutter-bezier.h"
27 #include "clutter-debug.h"
30 * We have some experimental code here to allow for constant velocity
31 * movement of actors along the bezier path, this macro enables it.
33 #undef CBZ_L2T_INTERPOLATION
35 /****************************************************************************
36 * ClutterBezier -- represenation of a cubic bezier curve *
37 * (private; a building block for the public bspline object) *
38 ****************************************************************************/
41 * The t parameter of the bezier is from interval <0,1>, so we can use
42 * 14.18 format and special multiplication functions that preserve
43 * more of the least significant bits but would overflow if the value
47 #define CBZ_T_ONE (1 << CBZ_T_Q)
48 #define CBZ_T_MUL(x,y) ((((x) >> 3) * ((y) >> 3)) >> 12)
49 #define CBZ_T_POW2(x) CBZ_T_MUL (x, x)
50 #define CBZ_T_POW3(x) CBZ_T_MUL (CBZ_T_POW2 (x), x)
51 #define CBZ_T_DIV(x,y) ((((x) << 9)/(y)) << 9)
54 * Constants for sampling of the bezier
56 #define CBZ_T_SAMPLES 128
57 #define CBZ_T_STEP (CBZ_T_ONE / CBZ_T_SAMPLES)
58 #define CBZ_L_STEP (CBZ_T_ONE / CBZ_T_SAMPLES)
60 typedef gint32 _FixedT;
63 * This is a private type representing a single cubic bezier
68 * bezier coefficients -- these are calculated using multiplication and
69 * addition from integer input, so these are also integers
81 /* length of the bezier */
84 #ifdef CBZ_L2T_INTERPOLATION
86 * coefficients for the L -> t bezier; these are calculated from fixed
87 * point input, and more specifically numbers that have been normalised
88 * to fit <0,1>, so these are also fixed point, and we can used the
94 /* _FixedT Ld; == 0 */
99 _clutter_bezier_new (void)
101 return g_slice_new0 (ClutterBezier);
105 _clutter_bezier_free (ClutterBezier * b)
109 g_slice_free (ClutterBezier, b);
114 _clutter_bezier_clone_and_move (const ClutterBezier *b, gint x, gint y)
116 ClutterBezier * b2 = _clutter_bezier_new ();
117 memcpy (b2, b, sizeof (ClutterBezier));
125 #ifdef CBZ_L2T_INTERPOLATION
127 * L is relative advance along the bezier curve from interval <0,1>
130 _clutter_bezier_L2t (const ClutterBezier *b, _FixedT L)
132 _FixedT t = CBZ_T_MUL (b->La, CBZ_T_POW3(L))
133 + CBZ_T_MUL (b->Lb, CBZ_T_POW2(L))
134 + CBZ_T_MUL (b->Lc, L);
146 _clutter_bezier_t2x (const ClutterBezier * b, _FixedT t)
149 * NB -- the int coefficients can be at most 8192 for the multiplication
150 * to work in this fashion due to the limits of the 14.18 fixed.
152 return ((b->ax*CBZ_T_POW3(t) + b->bx*CBZ_T_POW2(t) + b->cx*t) >> CBZ_T_Q)
157 _clutter_bezier_t2y (const ClutterBezier * b, _FixedT t)
160 * NB -- the int coefficients can be at most 8192 for the multiplication
161 * to work in this fashion due to the limits of the 14.18 fixed.
163 return ((b->ay*CBZ_T_POW3(t) + b->by*CBZ_T_POW2(t) + b->cy*t) >> CBZ_T_Q)
168 * Advances along the bezier to relative length L and returns the coordinances
172 _clutter_bezier_advance (const ClutterBezier *b, gint L, ClutterKnot * knot)
174 #ifdef CBZ_L2T_INTERPOLATION
175 _FixedT t = clutter_bezier_L2t (b, L);
180 knot->x = _clutter_bezier_t2x (b, t);
181 knot->y = _clutter_bezier_t2y (b, t);
183 CLUTTER_NOTE (MISC, "advancing to relative pt %f: t %f, {%d,%d}",
184 (double) L / (double) CBZ_T_ONE,
185 (double) t / (double) CBZ_T_ONE,
190 _clutter_bezier_init (ClutterBezier *b,
200 _FixedT length [CBZ_T_SAMPLES + 1];
202 #ifdef CBZ_L2T_INTERPOLATION
205 _FixedT t_equalized [CBZ_T_SAMPLES + 1];
209 g_debug ("Initializing bezier at {{%d,%d},{%d,%d},{%d,%d},{%d,%d}}",
210 x0, y0, x1, y1, x2, y2, x3, y3);
216 b->cx = 3 * (x_1 - x_0);
217 b->cy = 3 * (y_1 - y_0);
219 b->bx = 3 * (x_2 - x_1) - b->cx;
220 b->by = 3 * (y_2 - y_1) - b->cy;
222 b->ax = x_3 - 3 * x_2 + 3 * x_1 - x_0;
223 b->ay = y_3 - 3 * y_2 + 3 * y_1 - y_0;
226 g_debug ("Cooeficients {{%d,%d},{%d,%d},{%d,%d},{%d,%d}}",
227 b->ax, b->ay, b->bx, b->by, b->cx, b->cy, b->dx, b->dy);
231 * Because of the way we do the multiplication in bezeir_t2x,y
232 * these coefficients need to be at most 0x1fff; this should be the case,
233 * I think, but have added this warning to catch any problems -- if it
234 * triggers, we need to change those two functions a bit.
236 if (b->ax > 0x1fff || b->bx > 0x1fff || b->cx > 0x1fff)
237 g_warning ("Calculated coefficents will result in multiplication "
238 "overflow in clutter_bezier_t2x and clutter_bezier_t2y.");
241 * Sample the bezier with CBZ_T_SAMPLES and calculate length at
244 * We are working with integers here, so we use the fast sqrti function.
248 for (t = CBZ_T_STEP, i = 1; i <= CBZ_T_SAMPLES; ++i, t += CBZ_T_STEP)
250 int x = _clutter_bezier_t2x (b, t);
251 int y = _clutter_bezier_t2y (b, t);
253 guint l = cogl_sqrti ((y - yp)*(y - yp) + (x - xp)*(x - xp));
263 b->length = length[CBZ_T_SAMPLES];
266 g_debug ("length %d", b->length);
269 #ifdef CBZ_L2T_INTERPOLATION
271 * Now normalize the length values, converting them into _FixedT
273 for (i = 0; i <= CBZ_T_SAMPLES; ++i)
275 length[i] = (length[i] << CBZ_T_Q) / b->length;
279 * Now generate a L -> t table such that the L will equidistant
284 for (i = 1, j = 1, L = CBZ_L_STEP; i < CBZ_T_SAMPLES; ++i, L += CBZ_L_STEP)
290 /* find the band for our L */
291 for (k = j; k < CBZ_T_SAMPLES; ++k)
298 * Now we know that L is from (length[k-1],length[k]>
299 * We remember k-1 in order not to have to iterate over the
300 * whole length array in the next iteration of the main loop
305 * Now interpolate equlised t as a weighted average
312 t1 = (k - 1) * CBZ_T_STEP;
315 t_equalized[i] = (t1*d1 + t2*d2)/d;
317 if (t_equalized[i] < t_equalized[i-1])
318 g_debug ("wrong t: L %f, l1 %f, l2 %f, t1 %f, t2 %f",
319 (double) (L)/(double)CBZ_T_ONE,
320 (double) (l1)/(double)CBZ_T_ONE,
321 (double) (l2)/(double)CBZ_T_ONE,
322 (double) (t1)/(double)CBZ_T_ONE,
323 (double) (t2)/(double)CBZ_T_ONE);
327 t_equalized[CBZ_T_SAMPLES] = CBZ_T_ONE;
329 /* We now fit a bezier -- at this stage, do a single fit through our values
330 * at 0, 1/3, 2/3 and 1
332 * FIXME -- do we need to use a better fitting approach to choose the best
333 * beziere. The actual curve we acquire this way is not too bad shapwise,
334 * but (probably due to rounding errors) the resulting curve no longer
335 * satisfies the necessary condition that for L2 > L1, t2 > t1, which
341 * These are the control points we use to calculate the curve coefficients
342 * for bezier t(L); these are not needed directly, but are implied in the
343 * calculations below.
345 * (p0 is 0,0, and p3 is 1,1)
347 p1 = (18 * t_equalized[CBZ_T_SAMPLES/3] -
348 9 * t_equalized[2*CBZ_T_SAMPLES/3] +
351 p2 = (18 * t_equalized[2*CBZ_T_SAMPLES/3] -
352 9 * t_equalized[CBZ_T_SAMPLES/3] -
356 b->Lc = (18 * t_equalized[CBZ_T_SAMPLES/3] -
357 9 * t_equalized[2*CBZ_T_SAMPLES/3] +
358 (2 << CBZ_T_Q)) >> 1;
360 b->Lb = (36 * t_equalized[2*CBZ_T_SAMPLES/3] -
361 45 * t_equalized[CBZ_T_SAMPLES/3] -
362 (9 << CBZ_T_Q)) >> 1;
364 b->La = ((27 * (t_equalized[CBZ_T_SAMPLES/3] -
365 t_equalized[2*CBZ_T_SAMPLES/3]) +
366 (7 << CBZ_T_Q)) >> 1) + CBZ_T_ONE;
368 g_debug ("t(1/3) %f, t(2/3) %f",
369 (double)t_equalized[CBZ_T_SAMPLES/3]/(double)CBZ_T_ONE,
370 (double)t_equalized[2*CBZ_T_SAMPLES/3]/(double)CBZ_T_ONE);
372 g_debug ("L -> t coefficients: %f, %f, %f",
373 (double)b->La/(double)CBZ_T_ONE,
374 (double)b->Lb/(double)CBZ_T_ONE,
375 (double)b->Lc/(double)CBZ_T_ONE);
379 * For debugging, you can load these values into a spreadsheet and graph
380 * them to see how well the approximation matches the data
382 for (i = 0; i < CBZ_T_SAMPLES; ++i)
384 g_print ("%f, %f, %f\n",
385 (double)(i*CBZ_T_STEP)/(double)CBZ_T_ONE,
386 (double)(t_equalized[i])/(double)CBZ_T_ONE,
387 (double)(clutter_bezier_L2t(b,i*CBZ_T_STEP))/(double)CBZ_T_ONE);
393 * Moves a control point at indx to location represented by knot
396 _clutter_bezier_adjust (ClutterBezier * b, ClutterKnot * knot, guint indx)
405 x[1] = b->cx / 3 + x[0];
406 y[1] = b->cy / 3 + y[0];
408 x[2] = b->bx / 3 + b->cx + x[1];
409 y[2] = b->by / 3 + b->cy + y[1];
411 x[3] = b->ax + x[0] + b->cx + b->bx;
412 y[3] = b->ay + y[0] + b->cy + b->by;
417 _clutter_bezier_init (b, x[0], y[0], x[1], y[1], x[2], y[2], x[3], y[3]);
421 _clutter_bezier_get_length (const ClutterBezier *b)