f9fae684fa5aa60543030d8eaab4a5be3b77c375
[synfig.git] / synfig-core / src / synfig / time.cpp
1 /* === S Y N F I G ========================================================= */
2 /*!     \file time.cpp
3 **      \brief Template File
4 **
5 **      $Id$
6 **
7 **      \legal
8 **      Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
9 **      Copyright (c) 2007, 2008 Chris Moore
10 **  Copyright (c) 2008 Gerco Ballintijn
11 **  Copyright (c) 2008 Carlos López
12 **
13 **      This package is free software; you can redistribute it and/or
14 **      modify it under the terms of the GNU General Public License as
15 **      published by the Free Software Foundation; either version 2 of
16 **      the License, or (at your option) any later version.
17 **
18 **      This package is distributed in the hope that it will be useful,
19 **      but WITHOUT ANY WARRANTY; without even the implied warranty of
20 **      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 **      General Public License for more details.
22 **      \endlegal
23 */
24 /* ========================================================================= */
25
26 /* === H E A D E R S ======================================================= */
27
28 #ifdef USING_PCH
29 #       include "pch.h"
30 #else
31 #ifdef HAVE_CONFIG_H
32 #       include <config.h>
33 #endif
34
35 #include "time.h"
36 #include <ETL/stringf>
37 #include <ETL/misc>
38 #include "general.h"
39 #include <cmath>
40 #include <cassert>
41 #include <algorithm>
42 #include <cstdio>
43 #include <ctype.h>
44 #include <math.h>
45
46
47 #ifdef WIN32
48 #include <float.h>
49 #ifndef isnan
50 extern "C" { int _isnan(double x); }
51 #define isnan _isnan
52 #endif
53 #endif
54
55 // For some reason isnan() isn't working on macosx any more.
56 // This is a quick fix.
57 #if defined(__APPLE__) && !defined(SYNFIG_ISNAN_FIX)
58 #ifdef isnan
59 #undef isnan
60 #endif
61 inline bool isnan(double x) { return x != x; }
62 inline bool isnan(float x) { return x != x; }
63 #define SYNFIG_ISNAN_FIX 1
64 #endif
65
66
67 #endif
68
69 /* === U S I N G =========================================================== */
70
71 using namespace std;
72 using namespace etl;
73 using namespace synfig;
74
75 #define tolower ::tolower
76
77 /* === M A C R O S ========================================================= */
78
79 /* === G L O B A L S ======================================================= */
80
81 /* === M E T H O D S ======================================================= */
82
83 Time::Time(const String &str_, float fps):
84         value_(0)
85 {
86         String str(str_);
87         std::transform(str.begin(),str.end(),str.begin(),&tolower);
88
89         // Start/Begin Of Time
90         if(str=="sot" || str=="bot")
91         {
92                 operator=(begin());
93                 return;
94         }
95         // End Of Time
96         if(str=="eot")
97         {
98                 operator=(end());
99                 return;
100         }
101
102
103         unsigned int pos=0;
104         int read;
105         float amount;
106
107         // Now try to read it in the letter-abbreviated format
108         while(pos<str.size() && sscanf(String(str,pos).c_str(),"%f%n",&amount,&read))
109         {
110                 pos+=read;
111                 if(pos>=str.size() || read==0)
112                 {
113                         // Throw up a warning if there are no units
114                         // and the amount isn't zero. There is no need
115                         // to warn about units if the value is zero
116                         // it is the only case where units are irrelevant.
117                         if(amount!=0)
118                                 synfig::warning("Time(): No unit provided in time code, assuming SECONDS (\"%s\")",str.c_str());
119                         value_+=amount;
120                         return;
121                 }
122                 switch(str[pos])
123                 {
124                         case 'h':
125                         case 'H':
126                                 value_+=amount*3600;
127                                 break;
128                         case 'm':
129                         case 'M':
130                                 value_+=amount*60;
131                                 break;
132                         case 's':
133                         case 'S':
134                                 value_+=amount;
135                                 break;
136                         case 'f':
137                         case 'F':
138                                 if(fps)
139                                         value_+=amount/fps;
140                                 else
141                                         synfig::warning("Time(): Individual frames referenced, but frame rate is unknown");
142                                 break;
143                         case ':':
144                                 // try to read it in as a traditional time format
145                                 {
146                                         int hour,minute,second;
147                                         float frame;
148                                         if(fps && sscanf(str.c_str(),"%d:%d:%d.%f",&hour,&minute,&second,&frame)==4)
149                                         {
150                                                         value_=frame/fps+(hour*3600+minute*60+second);
151                                                         return;
152                                         }
153
154                                         if(sscanf(str.c_str(),"%d:%d:%d",&hour,&minute,&second)==3)
155                                         {
156                                                 value_=hour*3600+minute*60+second;
157                                                 return;
158                                         }
159                                 }
160                                 synfig::warning("Time(): Bad time format");
161                                 break;
162
163                         default:
164                                 value_+=amount;
165                                 synfig::warning("Time(): Unexpected character '%c' when parsing time string \"%s\"",str[pos],str.c_str());
166                                 break;
167                 }
168                 pos++;
169                 amount=0;
170         }
171 }
172
173 String
174 Time::get_string(float fps, Time::Format format)const
175 {
176         Time time(*this);
177
178         if(time<=begin())
179                 return "SOT";   // Start Of Time
180         if(time>=end())
181                 return "EOT";   // End Of Time
182
183         if(fps<0)fps=0;
184
185         if(ceil(time.value_)-time.value_<epsilon_())
186                 time.value_=ceil(time.value_);
187
188         int hour = 0, minute = 0;
189         if(!(format<=FORMAT_FRAMES))
190         {
191                 hour=time/3600;time-=hour*3600;
192                 minute=time/60;time-=minute*60;
193         }
194         // <= is redefined, so this means "is the FORMAT_VIDEO bit set in the format?"
195         if(format<=FORMAT_VIDEO)
196         {
197                 int second;
198                 second=time;time-=second;
199
200                 if(fps && fps>1)
201                 {
202                         int frame;
203                         frame=round_to_int(time*fps);
204
205                         return strprintf("%02d:%02d:%02d.%02d",hour,minute,second,frame);
206                 }
207                 else
208                         return strprintf("%02d:%02d:%02d",hour,minute,second);
209         }
210
211         if (format <= FORMAT_FRAMES)
212         {
213                 if (fps && fps>0)
214                         return strprintf("%df", round_to_int(time * fps));
215                 else
216                         return strprintf("%ds", round_to_int(time * 1));
217         }
218
219         String ret;
220         bool started = false;
221
222         if(format<=FORMAT_FULL || hour)
223         {
224                 ret+=strprintf("%dh",hour);
225                 started = true;
226         }
227
228         if(format<=FORMAT_FULL || minute)
229         {
230                 if (!(format<=FORMAT_NOSPACES) && started)
231                         ret += " ";
232
233                 ret += strprintf("%dm", minute);
234                 started = true;
235         }
236
237         if(fps && fps>1)
238         {
239                 int second;
240                 float frame;
241                 second=time;time-=second;
242                 frame=time*fps;
243
244                 if(format<=FORMAT_FULL || second)
245                 {
246                         if (!(format<=FORMAT_NOSPACES) && started)
247                                 ret += " ";
248
249                         ret += strprintf("%ds", (int)second);
250                         started = true;
251                 }
252
253                 if(format<=FORMAT_FULL || abs(frame) > epsilon_() || !started)
254                 {
255                         if (!(format<=FORMAT_NOSPACES) && started)
256                                 ret += " ";
257
258                         if(abs(frame-floor(frame) >= epsilon_()))
259                                 ret += strprintf("%0.3ff", frame);
260                         else
261                                 ret += strprintf("%0.0ff", frame);
262                 }
263         }
264         else
265         {
266                 float second;
267                 second=time;
268                 if(format<=FORMAT_FULL || second || !started)
269                 {
270                         if (!(format<=FORMAT_NOSPACES) && started)
271                                 ret += " ";
272
273                         if(abs(second-floor(second))>=epsilon_())
274                         {
275                                 String seconds(strprintf("%0.8f",second));
276
277                                 // skip trailing zeros
278                                 int count = 0;
279                                 String::reverse_iterator i = seconds.rbegin();
280                                 for ( ; (*i) == '0'; i++)
281                                         count++;
282
283                                 // if we removed too many, go back one place, leaving one zero
284                                 if (*i < '0' || *i > '9') count--;
285
286                                 ret += seconds.substr(0, seconds.size()-count) + "s";
287                         }
288                         else
289                                 ret+=strprintf("%0.0fs",second);
290                 }
291         }
292
293         return ret;
294 }
295
296 Time
297 Time::round(float fps)const
298 {
299         assert(fps>0);
300
301         value_type time(*this);
302
303         time*=fps;
304
305         if(abs(time-floor(time))<0.5)
306                 return floor(time)/fps;
307         else
308                 return ceil(time)/fps;
309 }
310
311 #ifdef _DEBUG
312 const char *
313 Time::c_str()const
314 {
315         return get_string().c_str();
316 }
317 #endif
318
319 //! \writeme
320 bool
321 Time::is_valid()const
322 {
323         return !isnan(value_);
324 }