Back to index

texmacs  1.0.7.15
animate_boxes.cpp
Go to the documentation of this file.
00001 
00002 /******************************************************************************
00003 * MODULE     : animate_boxes.cpp
00004 * DESCRIPTION: animations
00005 * COPYRIGHT  : (C) 2005  Joris van der Hoeven
00006 *******************************************************************************
00007 * This software falls under the GNU general public license version 3 or later.
00008 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
00009 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
00010 ******************************************************************************/
00011 
00012 #include "Boxes/composite.hpp"
00013 #include "Boxes/construct.hpp"
00014 #include "file.hpp"
00015 
00016 /******************************************************************************
00017 * Global animation tracking
00018 ******************************************************************************/
00019 
00020 bool   refresh_needed= false;
00021 time_t refresh_next  = 0;
00022 
00023 void
00024 refresh_at (time_t t) {
00025   time_t now= texmacs_time ();
00026   if (t - now < 0) t= now;
00027   if (refresh_needed) {
00028     if (refresh_next - now < 0) refresh_next= now;
00029     if (t - refresh_next < 0) refresh_next= t;
00030   }
00031   else {
00032     refresh_needed= true;
00033     refresh_next  = t;
00034   }
00035   //cout << "Refresh at " << t << " -> " << refresh_next << "\n";
00036 }
00037 
00038 /******************************************************************************
00039 * Animations which remain constant for a fixed duration
00040 ******************************************************************************/
00041 
00042 struct anim_constant_box_rep: public composite_box_rep {
00043   bool   started;
00044   time_t started_at;
00045   bool   finished;
00046   int    length;
00047 
00048   anim_constant_box_rep (path ip, box b, int length);
00049   operator tree () { return tree (TUPLE, "anim_constant", (tree) bs[0]); }
00050 
00051   void   pre_display (renderer& ren);
00052   int    anim_length () { return length; }
00053   bool   anim_started () { return started; }
00054   bool   anim_finished () { return finished; }
00055   void   anim_start_at (time_t at);
00056   void   anim_finish_now ();
00057   time_t anim_next_update () { return started_at + length; }
00058   void   anim_get_invalid (bool& flag, time_t& at, rectangles& rs);
00059 };
00060 
00061 anim_constant_box_rep::anim_constant_box_rep (path ip, box b, int length2):
00062   composite_box_rep (ip), length (length2)
00063 {
00064   insert (b, 0, 0);
00065   position ();
00066   finalize ();
00067   started= finished= false;
00068 }
00069 
00070 void
00071 anim_constant_box_rep::pre_display (renderer& ren) {
00072   (void) ren;
00073   if (!started) anim_start_at (texmacs_time ());
00074   else if (!finished) {
00075     finished= (texmacs_time () - (started_at+length) >= 0);
00076   }
00077   if (!finished) refresh_at (anim_next_update ());
00078 }
00079 
00080 void
00081 anim_constant_box_rep::anim_start_at (time_t at) {
00082   started   = true;
00083   started_at= at;
00084   finished  = (length == 0);
00085   bs[0]->anim_start_at (at);
00086 }
00087 
00088 void
00089 anim_constant_box_rep::anim_finish_now () {
00090   bs[0]->anim_finish_now ();
00091   started= finished= true;
00092 }
00093 
00094 void
00095 anim_constant_box_rep::anim_get_invalid (bool& f, time_t& at, rectangles& rs) {
00096   if (started && !finished) {
00097     bs[0]->anim_get_invalid (f, at, rs);
00098     anim_check_invalid (f, at, rs);
00099   }
00100 }
00101 
00102 /******************************************************************************
00103 * Compositions of animations
00104 ******************************************************************************/
00105 
00106 class anim_compose_box_rep: public box_rep {
00107 public:
00108   bool       started;
00109   time_t     started_at;
00110   int        current;
00111   bool       finished;
00112   array<box> bs;
00113   array<int> cum_len;
00114 
00115   anim_compose_box_rep (path ip, array<box> bs);
00116   ~anim_compose_box_rep ();
00117 
00118   int       subnr () { return 1; }
00119   box       subbox (int i) { (void) i; return bs[current]; }
00120   void      display (renderer ren) { (void) ren; }
00121   operator  tree () { return tree ("composed animation"); }
00122   tree      action (tree t, SI x, SI y, SI delta);
00123   void      loci (SI x, SI y, SI delta, list<string>& ids, rectangles& rs);
00124   void      collect_page_numbers (hashmap<string,tree>& h, tree page);
00125   path      find_tag (string name);
00126 
00127   void   pre_display (renderer& ren);
00128   int    anim_length () { return cum_len[N(bs)-1]; }
00129   bool   anim_started () { return started; }
00130   bool   anim_finished () { return finished; }
00131   void   anim_start_at (time_t at);
00132   void   anim_finish_now ();
00133   time_t anim_next_update () { return started_at + cum_len[current]; }
00134   void   anim_get_invalid (bool& flag, time_t& at, rectangles& rs);
00135 
00136   path          find_box_path (SI x, SI y, SI delta, bool force);
00137   path          find_box_path (path p, bool& found);
00138   path          find_tree_path (path bp);
00139   cursor        find_cursor (path bp);
00140   selection     find_selection (path lbp, path rbp);
00141   gr_selections graphical_select (SI x, SI y, SI dist);
00142 };
00143 
00144 /******************************************************************************
00145 * Composition of animations / basic routines
00146 ******************************************************************************/
00147 
00148 anim_compose_box_rep::anim_compose_box_rep (path ip, array<box> bs2):
00149   box_rep (ip), bs (bs2), cum_len (N(bs))
00150 {
00151   ASSERT (N(bs) != 0, "empty animation");
00152 
00153   started = false;
00154   finished= false;
00155   current = 0;
00156 
00157   int i, n= N(bs);
00158   x1= y1= x3= y3= MAX_SI;
00159   x2= y2= x4= y4= -MAX_SI;
00160   for (i=0; i<n; i++) {
00161     x1= min (x1, bs[i]->x1);
00162     y1= min (y1, bs[i]->y1);
00163     x2= max (x2, bs[i]->x2);
00164     y2= max (y2, bs[i]->y2);
00165     x3= min (x3, bs[i]->x3);
00166     y3= min (y3, bs[i]->y3);
00167     x4= max (x4, bs[i]->x4);
00168     y4= max (y4, bs[i]->y4);
00169   }
00170 
00171   int len= 0;
00172   for (i=0; i<n; i++) {
00173     int sl= bs[i]->anim_length ();
00174     if (sl == -1) len= -1;
00175     if (len != -1) len += sl;
00176     cum_len[i]= len;
00177   }
00178 }
00179 
00180 anim_compose_box_rep::~anim_compose_box_rep () {}
00181 
00182 tree
00183 anim_compose_box_rep::action (tree t, SI x, SI y, SI delta) {
00184   return bs[current]->action (t, x, y, delta);
00185 }
00186 
00187 void
00188 anim_compose_box_rep::loci (SI x, SI y, SI delta,
00189                          list<string>& ids, rectangles& rs)
00190 {
00191   bs[current]->loci (x, y, delta, ids, rs);
00192 }
00193 
00194 void
00195 anim_compose_box_rep::collect_page_numbers (
00196   hashmap<string,tree>& h, tree page)
00197 {
00198   bs[current]->collect_page_numbers (h, page);
00199 }
00200 
00201 path
00202 anim_compose_box_rep::find_tag (string name) {
00203   return bs[current]->find_tag (name);
00204 }
00205 
00206 /******************************************************************************
00207 * Compositions of animations / animation routines
00208 ******************************************************************************/
00209 
00210 void
00211 anim_compose_box_rep::pre_display (renderer& ren) {
00212   (void) ren;
00213   if (!started) anim_start_at (texmacs_time ());
00214   else if (!finished) {
00215     time_t now= texmacs_time ();
00216     while (current < N(bs) && now - (started_at+cum_len[current]) >= 0) {
00217       bs[current]->anim_finish_now ();
00218       current++;
00219       if (current<N(bs))
00220        bs[current]->anim_start_at (started_at + cum_len[current-1]);
00221     }
00222     if (current == N(bs)) {
00223       finished= true;
00224       current--;
00225     }
00226   }
00227   if (!finished) refresh_at (anim_next_update ());
00228 }
00229 
00230 void
00231 anim_compose_box_rep::anim_start_at (time_t at) {
00232   started   = true;
00233   started_at= at;
00234   finished  = (anim_length () == 0);
00235   current   = 0;
00236   bs[current]->anim_start_at (at);
00237 }
00238 
00239 void
00240 anim_compose_box_rep::anim_finish_now () {
00241   int i, n= N(bs);
00242   for (i=current; i<n; i++) {
00243     bs[i]->anim_finish_now ();
00244     if (i+1 < n)
00245       bs[i+1]->anim_start_at (started_at + cum_len[i]);
00246   }
00247   current= n-1;
00248   started= finished= true;
00249 }
00250 
00251 void
00252 anim_compose_box_rep::anim_get_invalid (bool& f, time_t& at, rectangles& rs) {
00253   if (started && !finished) {
00254     bs[current]->anim_get_invalid (f, at, rs);
00255     anim_check_invalid (f, at, rs);
00256   }
00257 }
00258 
00259 /******************************************************************************
00260 * Compositions of animations / cursor routines
00261 ******************************************************************************/
00262 
00263 path
00264 anim_compose_box_rep::find_box_path (SI x, SI y, SI delta, bool force) {
00265   if (outside (x, delta, x1, x2) && (is_accessible (ip) || force))
00266     return box_rep::find_box_path (x, y, delta, force);
00267   return path (0, bs[current]->find_box_path (x, y, delta, force));
00268 }
00269 
00270 path
00271 anim_compose_box_rep::find_box_path (path p, bool& found) {
00272   path r= path (0, bs[current]->find_box_path (p, found));
00273   if (found) return r;
00274   return box_rep::find_box_path (p, found);
00275 }
00276 
00277 path
00278 anim_compose_box_rep::find_tree_path (path bp) {
00279   if (is_atom (bp)) return box_rep::find_tree_path (bp);
00280   return bs[current]->find_tree_path (bp->next);
00281 }
00282 
00283 cursor
00284 anim_compose_box_rep::find_cursor (path bp) {
00285   if (is_atom (bp)) return box_rep::find_cursor (bp);
00286   else return bs[current]->find_cursor (bp->next);
00287 }
00288 
00289 selection
00290 anim_compose_box_rep::find_selection (path lbp, path rbp) {
00291   if (!is_atom (lbp) && !is_atom (rbp))
00292     return bs[current]->find_selection (lbp->next, rbp->next);
00293   else return box_rep::find_selection (lbp, rbp);
00294 }
00295 
00296 gr_selections
00297 anim_compose_box_rep::graphical_select (SI x, SI y, SI dist) {
00298   return bs[current]->graphical_select (x- sx(0), y- sy(0), dist);
00299 }
00300 
00301 /******************************************************************************
00302 * Animations which are repeated ad infinam
00303 ******************************************************************************/
00304 
00305 struct anim_repeat_box_rep: public composite_box_rep {
00306   bool   started;
00307   time_t started_at;
00308   int    length;
00309 
00310   anim_repeat_box_rep (path ip, box b);
00311   operator tree () { return tree (TUPLE, "anim_repeat", (tree) bs[0]); }
00312 
00313   void pre_display (renderer& ren);
00314   int  anim_length () { return -1; }
00315   bool anim_started () { return started; }
00316   bool anim_finished () { return false; }
00317   void anim_start_at (time_t at);
00318   void anim_finish_now () {}
00319   void anim_get_invalid (bool& flag, time_t& at, rectangles& rs);
00320 };
00321 
00322 anim_repeat_box_rep::anim_repeat_box_rep (path ip, box b):
00323   composite_box_rep (ip)
00324 {
00325   insert (b, 0, 0);
00326   position ();
00327   finalize ();
00328   started = false;
00329   length  = b->anim_length ();
00330 }
00331 
00332 void
00333 anim_repeat_box_rep::pre_display (renderer& ren) {
00334   (void) ren;
00335   if (!started) anim_start_at (texmacs_time ());
00336   else if (length > 0) {
00337     time_t now= texmacs_time ();
00338     if (now - (started_at+length) >= 0) {
00339       bs[0]->anim_finish_now ();
00340       while (now - (started_at+length) >= 0)
00341        started_at += length;
00342       bs[0]->anim_start_at (started_at);
00343     }
00344   }
00345 }
00346 
00347 void
00348 anim_repeat_box_rep::anim_start_at (time_t at) {
00349   started= true;
00350   started_at= at;
00351   bs[0]->anim_start_at (at);
00352 }
00353 
00354 void
00355 anim_repeat_box_rep::anim_get_invalid (bool& f, time_t& at, rectangles& rs) {
00356   if (started) bs[0]->anim_get_invalid (f, at, rs);
00357 }
00358 
00359 /******************************************************************************
00360 * Special content effects
00361 ******************************************************************************/
00362 
00363 struct anim_effect_box_rep: public composite_box_rep {
00364   box    b;
00365   bool   started;
00366   bool   finished;
00367   time_t started_at;
00368   time_t last_update;
00369   int    length;
00370   SI     old_clip_x1, old_clip_x2, old_clip_y1, old_clip_y2;
00371 
00372   anim_effect_box_rep (path ip, box b, int len);
00373   operator tree () { return tree (TUPLE, "anim_effect", (tree) b); }
00374 
00375   void pre_display (renderer& ren);
00376   void post_display (renderer& ren);
00377   virtual void set_position (double t) = 0;
00378   virtual void set_clipping (renderer& ren, double t) = 0;
00379 
00380   int    anim_length () { return length; }
00381   bool   anim_started () { return started; }
00382   bool   anim_finished () { return finished; }
00383   void   anim_start_at (time_t at);
00384   void   anim_finish_now ();
00385   time_t anim_next_update ();
00386   void   anim_get_invalid (bool& flag, time_t& at, rectangles& rs);
00387 };
00388 
00389 anim_effect_box_rep::anim_effect_box_rep (path ip, box b2, int len):
00390   composite_box_rep (ip), b (b2)
00391 {
00392   insert (b, 0, 0);
00393   position ();
00394   finalize ();
00395   started = false;
00396   finished= false;
00397   length  = len;
00398 }
00399 
00400 void
00401 anim_effect_box_rep::pre_display (renderer& ren) {
00402   double t= 1.0;
00403   if (!started) anim_start_at (texmacs_time ());
00404 
00405   if (!finished) {
00406     time_t now= texmacs_time ();
00407     if (now - (started_at + length) >= 0) {
00408       t= 1.0;
00409       anim_finish_now ();
00410     }
00411     else {
00412       t= ((double) (now - started_at)) / ((double) length);
00413       set_position (t);
00414     }
00415     last_update= now;
00416     refresh_at (anim_next_update ());
00417   }
00418 
00419   ren->get_clipping (old_clip_x1, old_clip_y1, old_clip_x2, old_clip_y2);
00420   set_clipping (ren, t);
00421 }
00422 
00423 void
00424 anim_effect_box_rep::post_display (renderer &ren) {
00425   ren->set_clipping
00426     (old_clip_x1, old_clip_y1, old_clip_x2, old_clip_y2, true);
00427 }
00428 
00429 void
00430 anim_effect_box_rep::anim_start_at (time_t at) {
00431   started   = true;
00432   started_at= at;
00433   finished  = false;
00434   bs[0]->anim_start_at (at);
00435   set_position (0.0);
00436 }
00437 
00438 void
00439 anim_effect_box_rep::anim_finish_now () {
00440   started = true;
00441   finished= true;
00442   bs[0]->anim_finish_now ();
00443   set_position (1.0);
00444 }
00445 
00446 time_t
00447 anim_effect_box_rep::anim_next_update () {
00448   int elapsed= last_update - started_at;
00449   return started_at + min (40 * ((elapsed / 40) + 1), length);
00450 }
00451 
00452 void
00453 anim_effect_box_rep::anim_get_invalid
00454   (bool& f, time_t& at, rectangles& rs)
00455 {
00456   if (started) {
00457     bs[0]->anim_get_invalid (f, at, rs);
00458     if (!finished) anim_check_invalid (f, at, rs);
00459   }
00460 }
00461 
00462 struct anim_translate_box_rep: public anim_effect_box_rep {
00463   SI start_x, start_y, end_x, end_y;
00464 
00465   anim_translate_box_rep (path ip, box b, int len, SI sx, SI sy, SI ex, SI ey):
00466     anim_effect_box_rep (ip, b, len),
00467     start_x (sx), start_y (sy), end_x (ex), end_y (ey) {}
00468   operator tree () { return tree (TUPLE, "anim_translate", (tree) b); }
00469 
00470   void set_position (double t) {
00471     sx (0)= start_x - x1 + as_int (t * (end_x - start_x));
00472     sy (0)= start_y - y1 + as_int (t * (end_y - start_y)); }
00473   void set_clipping (renderer& ren, double t) {
00474     if (t != 1.0 || end_x != x1 || end_y != y1)
00475       ren->extra_clipping (x1, y1, x2, y2); }
00476 };
00477 
00478 struct anim_progressive_box_rep: public anim_effect_box_rep {
00479   rectangle start_r, end_r;
00480   anim_progressive_box_rep (path ip, box b, int l, rectangle r1, rectangle r2):
00481     anim_effect_box_rep (ip, b, l),
00482     start_r (r1), end_r (r2) {}
00483   operator tree () { return tree (TUPLE, "anim_progressive", (tree) b); }
00484 
00485   void set_position (double t) { (void) t; }
00486   void set_clipping (renderer& ren, double t) {
00487     SI X1= start_r->x1 + as_int (t * (end_r->x1 - start_r->x1));
00488     SI Y1= start_r->y1 + as_int (t * (end_r->y1 - start_r->y1));
00489     SI X2= start_r->x2 + as_int (t * (end_r->x2 - start_r->x2));
00490     SI Y2= start_r->y2 + as_int (t * (end_r->y2 - start_r->y2));
00491     if (t != 1.0 || X1 != x1 || Y1 != y1 || X2 != x2 || Y2 != y2)
00492       ren->extra_clipping (X1, Y1, X2, Y2); }
00493 };
00494 
00495 /******************************************************************************
00496 * Sound boxes
00497 ******************************************************************************/
00498 
00499 struct sound_box_rep: public box_rep {
00500   url    u;
00501   bool   started;
00502 
00503   sound_box_rep (path ip, url u2, SI h):
00504     box_rep (ip), u (u2), started (false) { y2= h; }
00505   operator tree () { return tree (TUPLE, "sound", u->t); }
00506   void display (renderer ren) { (void) ren; }
00507 
00508   void play_sound () {
00509     if (exists_in_path ("play"))
00510       system ("play", u, "&");
00511     started= true; }
00512   void pre_display (renderer& ren) {
00513     (void) ren;
00514     if (!started) anim_start_at (texmacs_time ()); }
00515   int  anim_length () { return 0; }
00516   bool anim_started () { return started; }
00517   bool anim_finished () { return anim_started (); }
00518   void anim_start_at (time_t at) { (void) at; play_sound (); }
00519   void anim_finish_now () { if (!started) play_sound (); }
00520 };
00521 
00522 /******************************************************************************
00523 * Animated gifs
00524 ******************************************************************************/
00525 
00526 static hashmap<tree,tree> decomposed_gif ("");
00527 
00528 static url
00529 decompose_gif (url u) {
00530   if (!exists_in_path ("convert"))
00531     return url_none ();
00532   if (!decomposed_gif->contains (u->t)) {
00533     url tmp= url_temp ("");
00534     url res= glue (tmp, "_%04d.gif");
00535     system ("convert +adjoin -coalesce", u, res);
00536     decomposed_gif (u->t)= tmp->t;
00537   }
00538   url tmp= as_url (decomposed_gif [u->t]);
00539   url dir= head (tmp);
00540   url nam= tail (tmp);
00541   return expand (complete (dir * url_wildcard (as_string (nam) * "_*.gif")));
00542 }
00543 
00544 static void
00545 add_frames (array<box>& v, path ip, url u, int w, int h, int a, int msecs) {
00546   if (is_none (u)) return;
00547   else if (is_or (u)) {
00548     add_frames (v, ip, u[1], w, h, a, msecs);
00549     add_frames (v, ip, u[2], w, h, a, msecs);
00550   }
00551   else {
00552     box imb= image_box (ip, u, w, h, a);
00553     v << anim_constant_box (ip, imb, msecs);
00554   }
00555 }
00556 
00557 /******************************************************************************
00558 * box construction routines
00559 ******************************************************************************/
00560 
00561 box
00562 anim_constant_box (path ip, box b, int len) {
00563   return tm_new<anim_constant_box_rep> (ip, b, len);
00564 }
00565 
00566 box
00567 anim_compose_box (path ip, array<box> bs) {
00568   return tm_new<anim_compose_box_rep> (ip, bs);
00569 }
00570 
00571 box
00572 anim_repeat_box (path ip, box b) {
00573   return tm_new<anim_repeat_box_rep> (ip, b);
00574 }
00575 
00576 box
00577 anim_translate_box (path ip, box b, int len, SI sx, SI sy, SI ex, SI ey) {
00578   return tm_new<anim_translate_box_rep> (ip, b, len, sx, sy, ex, ey);
00579 }
00580 
00581 box
00582 anim_progressive_box (path ip, box b, int len, rectangle r1, rectangle r2) {
00583   return tm_new<anim_progressive_box_rep> (ip, b, len, r1, r2);
00584 }
00585 
00586 box
00587 sound_box (path ip, url u, SI h) {
00588   return tm_new<sound_box_rep> (ip, u, h);
00589 }
00590 
00591 box
00592 video_box (path ip, url u, SI w, SI h, int alpha, int msecs, bool repeated) {
00593   url frames= decompose_gif (u);
00594   if (is_none (frames)) return empty_box (ip, 0, 0, w, h);
00595   array<box> bs;
00596   add_frames (bs, decorate (ip), frames, w, h, alpha, msecs);
00597   box b= anim_compose_box (repeated? decorate (ip): ip, bs);
00598   if (repeated) return anim_repeat_box (ip, b);
00599   else return b;
00600 }