1 /* Copyright (C) 2008 Vincent Penquerc'h.
2    This file is part of the Kate codec library.
3    Written by Vincent Penquerc'h.
4 
5    Use, distribution and reproduction of this library is governed
6    by a BSD style source license included with this source in the
7    file 'COPYING'. Please read these terms before distributing. */
8 
9 
10 #define KATE_INTERNAL
11 #include "kate_internal.h"
12 
13 #ifdef HAVE_STDLIB_H
14 #include <stdlib.h>
15 #endif
16 #include "kate_meta.h"
17 #include "kate/kate.h"
18 
kate_catmull_rom(kate_float t,const kate_float * pts,int k0,int k1,int k2,int k3)19 static kate_float kate_catmull_rom(kate_float t,const kate_float *pts,int k0,int k1,int k2,int k3)
20 {
21   const kate_float t2=t*t,t3=t2*t;
22 
23   return (
24     (2*pts[k1*2])
25     + t * (pts[k2*2]-pts[k0*2])
26     + t2 * (-pts[k3*2]+4*pts[k2*2]-5*pts[k1*2]+2*pts[k0*2])
27     + t3 * (pts[k3*2]-3*pts[k2*2]+3*pts[k1*2]-pts[k0*2])
28   )/2;
29 }
30 
kate_bezier_cubic(kate_float t,const kate_float * pts)31 static kate_float kate_bezier_cubic(kate_float t,const kate_float *pts)
32 {
33   const kate_float t2=t*t,t3=t2*t,it=1-t,it2=it*it,it3=it2*it;
34 
35   return it3*pts[0*2]
36          +3*t*it2*pts[1*2]
37          +3*t2*it*pts[2*2]
38          +t3*pts[3*2];
39 }
40 
kate_bspline(kate_float t,const kate_float * pts,int k0,int k1,int k2,int k3)41 static kate_float kate_bspline(kate_float t,const kate_float *pts,int k0,int k1,int k2,int k3)
42 {
43   const kate_float t2=t*t,t3=t2*t,it=1-t,it2=it*it,it3=it2*it;
44 
45   return (
46            it3*pts[k0*2]
47            +(3*t3-6*t2+4)*pts[k1*2]
48            +(-3*t3+3*t2+3*t+1)*pts[k2*2]
49            +t3*pts[k3*2]
50          )/6;
51 }
52 
53 /**
54   Returns the point defined by the given curve at the given time.
55   t will be between 0 and 1
56   \param kc the curve to get the point from
57   \param t the time at which the point should be taken (between 0 and motion duration)
58   \param x a pointer to the first coordinate of the computed point (may be NULL)
59   \param y a pointer to the second coordinate of the computed point (may be NULL)
60   \returns 0 success
61   \returns 1 no point at this time in the curve
62   \returns KATE_E_* error
63   */
kate_curve_get_point(const kate_curve * kc,kate_float t,kate_float * x,kate_float * y)64 int kate_curve_get_point(const kate_curve *kc,kate_float t,kate_float *x,kate_float *y)
65 {
66   int nsegs,n;
67   kate_float T,t0,t1;
68 
69   if (!kc) return KATE_E_INVALID_PARAMETER;
70   if (t<(kate_float)-0.001 || t>(kate_float)1.001) return KATE_E_INVALID_PARAMETER;
71   if (t<0) t=0;
72   if (t>1) t=1;
73   /* x and y may be NULL */
74 
75   switch (kc->type) {
76     case kate_curve_none:
77       /* the motion can be discontinuous */
78       return 1;
79 
80     case kate_curve_static:
81       if (x) *x=kc->pts[0];
82       if (y) *y=kc->pts[1];
83       return 0;
84 
85     case kate_curve_linear:
86       /* find the segment we're on */
87       nsegs=kc->npts-1;
88       if (nsegs<1) return KATE_E_INIT;
89       n=t*nsegs;
90       /* ensure it doesn't overflow */
91       if (n<0) n=0;
92       if (n>=nsegs) n=nsegs-1;
93       /* get a 0-1 t on this segment */
94       t0=n/(kate_float)nsegs;
95       t1=(n+1)/(kate_float)nsegs;
96       T=(t-t0)/(t1-t0);
97       if (x) *x=T*kc->pts[(n+1)*2+0]+(1-T)*kc->pts[n*2+0];
98       if (y) *y=T*kc->pts[(n+1)*2+1]+(1-T)*kc->pts[n*2+1];
99       return 0;
100 
101     case kate_curve_catmull_rom_spline:
102       /* find the segment we're on */
103       nsegs=kc->npts-1; /* one segment less than points */
104       if (nsegs<1) return KATE_E_INIT;
105       n=t*nsegs;
106       /* ensure it doesn't overflow */
107       if (n<0) n=0;
108       if (n>=nsegs) n=nsegs-1;
109       /* get a 0-1 t on this segment */
110       t0=n/(kate_float)nsegs;
111       t1=(n+1)/(kate_float)nsegs;
112       T=(t-t0)/(t1-t0);
113       {
114         int k0=n-1;
115         int k1=n;
116         int k2=n+1;
117         int k3=n+2;
118         if (n==0) k0=n;
119         if (n==nsegs-1) k3=n+1;
120         if (x) *x=kate_catmull_rom(T,kc->pts,k0,k1,k2,k3);
121         if (y) *y=kate_catmull_rom(T,kc->pts+1,k0,k1,k2,k3);
122       }
123       return 0;
124 
125     case kate_curve_bezier_cubic_spline:
126       /* find the segment we're on */
127       if (kc->npts<4) return KATE_E_INIT;
128       if ((kc->npts-1)%3) return KATE_E_INIT;
129       nsegs=(kc->npts-1)/3; /* 4 control points per segment, with one shared */
130       n=t*nsegs;
131       /* ensure it doesn't overflow */
132       if (n<0) n=0;
133       if (n>=nsegs) n=nsegs-1;
134       /* get a 0-1 t on this segment */
135       t0=n/(kate_float)nsegs;
136       t1=(n+1)/(kate_float)nsegs;
137       T=(t-t0)/(t1-t0);
138       if (x) *x=kate_bezier_cubic(T,kc->pts+2*n*3);
139       if (y) *y=kate_bezier_cubic(T,kc->pts+2*n*3+1);
140       return 0;
141 
142     case kate_curve_bspline:
143       if (kc->npts<1) return KATE_E_INIT;
144       /* find the segment we're on */
145       nsegs=kc->npts+3; /* more segments than points, end knots are 3-repeated */
146       if (nsegs<1) return KATE_E_INIT;
147       n=t*nsegs;
148       /* ensure it doesn't overflow */
149       if (n<0) n=0;
150       if (n>=nsegs) n=nsegs-1;
151       /* get a 0-1 t on this segment */
152       t0=n/(kate_float)nsegs;
153       t1=(n+1)/(kate_float)nsegs;
154       T=(t-t0)/(t1-t0);
155       {
156         int k0=n-3;
157         int k1=k0+1;
158         int k2=k0+2;
159         int k3=k0+3;
160 #define clamp_knot(k) do { if (k<0) k=0; if (k>=(int)kc->npts) k=kc->npts-1; } while(0)
161         clamp_knot(k0);
162         clamp_knot(k1);
163         clamp_knot(k2);
164         clamp_knot(k3);
165 #undef clamp_knot
166         if (x) *x=kate_bspline(T,kc->pts,k0,k1,k2,k3);
167         if (y) *y=kate_bspline(T,kc->pts+1,k0,k1,k2,k3);
168       }
169       return 0;
170 
171     default:
172       return KATE_E_INVALID_PARAMETER;
173   }
174 }
175 
176 /**
177   Returns the point defined by the given motion at the given time.
178   t will be between 0 and the duration of the motion
179   \param km the motion to get the point from
180   \param duration the duration the motion spans
181   \param t the time at which the point should be taken (between 0 and motion duration)
182   \param x a pointer to the first coordinate of the computed point (may be NULL)
183   \param y a pointer to the second coordinate of the computed point (may be NULL)
184   \returns 0 success
185   \returns 1 no point at this time in the motion
186   \returns KATE_E_* error
187   */
kate_motion_get_point(const kate_motion * km,kate_float duration,kate_float t,kate_float * x,kate_float * y)188 int kate_motion_get_point(const kate_motion *km,kate_float duration,kate_float t,kate_float *x,kate_float *y)
189 {
190   size_t n;
191   kate_float motion_duration;
192 
193   if (!km) return KATE_E_INVALID_PARAMETER;
194   if (duration<0) return KATE_E_INVALID_PARAMETER;
195   if (t<0 || t>duration) return KATE_E_INVALID_PARAMETER;
196   /* x and y may be NULL */
197 
198   motion_duration=0;
199   do {
200     for (n=0;n<km->ncurves;++n) {
201       kate_float curve_duration=km->durations[n];
202       if (curve_duration<0.0) curve_duration=-curve_duration*duration;
203       if (t<=curve_duration) {
204         /* TODO: div by zero possible here */
205         return kate_curve_get_point(km->curves[n],t/curve_duration,x,y);
206       }
207       t-=curve_duration;
208       motion_duration+=curve_duration;
209     }
210     /* if the motion is periodic, we loop forever */
211     if (km->periodic) {
212       /* we now know the full duration, so we can remap t into 0..duration so next loop
213          will hit the point */
214       int loops=t/motion_duration;
215       t-=loops*motion_duration;
216     }
217   } while (km->periodic);
218 
219   /* t out of range */
220   return KATE_E_INVALID_PARAMETER;
221 }
222 
223 /**
224   Destroys a list of motions.
225   \param ki the kate_info structure containing the list of predefined motions
226   \param motions the list of motions to destroy
227   \param destroy a list of booleans specifying which motions in the list to destroy
228   \param nmotions the number of motions in the list
229   \param force if zero, motions found in the predefined motion list will not be destroyed
230   \returns 0 success
231   \returns KATE_E_* error
232   */
kate_motion_destroy(const kate_info * ki,kate_motion ** motions,const int * destroy,size_t nmotions,int force)233 int kate_motion_destroy(const kate_info *ki,kate_motion **motions,const int *destroy,size_t nmotions,int force)
234 {
235   size_t n,l;
236   kate_motion *km;
237 
238   if (!ki || !motions) return KATE_E_INVALID_PARAMETER;
239 
240   for (n=0;n<nmotions;++n) {
241     km=motions[n];
242     if (!km) continue; /* may have holes if referred by index */
243     if (destroy && !destroy[n]) continue;
244     if (force || kate_find_motion(ki,km)<0) {
245       if (km->curves) {
246         for (l=0;l<km->ncurves;++l) {
247           kate_curve *kc=km->curves[l];
248           if (kate_find_curve(ki,kc)<0) {
249             kate_free(kc->pts);
250             kate_free(kc);
251           }
252         }
253         kate_free(km->curves);
254       }
255       if (km->durations) kate_free(km->durations);
256       if (km->meta) kate_meta_destroy(km->meta);
257       kate_free(km);
258     }
259   }
260   kate_free(motions);
261 
262   return 0;
263 }
264 
265