Assume frames when no unit provided in a time field. Apply feature request ID: 2879693
[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 && fps)
118                         {
119                                 synfig::warning(_("Time(): No unit provided in time code, assuming FRAMES (\"%s\")"),str.c_str());
120                                 value_+=amount/fps;
121                         }
122                         else
123                         {
124                                 synfig::warning(_("Time(): No unit provided in time code and frame rate is unknown! Assuming SECONDS"));
125                                 value_+=amount;
126                         }
127                         return;
128                 }
129                 switch(str[pos])
130                 {
131                         case 'h':
132                         case 'H':
133                                 value_+=amount*3600;
134                                 break;
135                         case 'm':
136                         case 'M':
137                                 value_+=amount*60;
138                                 break;
139                         case 's':
140                         case 'S':
141                                 value_+=amount;
142                                 break;
143                         case 'f':
144                         case 'F':
145                                 if(fps)
146                                         value_+=amount/fps;
147                                 else
148                                         synfig::warning("Time(): Individual frames referenced, but frame rate is unknown");
149                                 break;
150                         case ':':
151                                 // try to read it in as a traditional time format
152                                 {
153                                         int hour,minute,second;
154                                         float frame;
155                                         if(fps && sscanf(str.c_str(),"%d:%d:%d.%f",&hour,&minute,&second,&frame)==4)
156                                         {
157                                                         value_=frame/fps+(hour*3600+minute*60+second);
158                                                         return;
159                                         }
160
161                                         if(sscanf(str.c_str(),"%d:%d:%d",&hour,&minute,&second)==3)
162                                         {
163                                                 value_=hour*3600+minute*60+second;
164                                                 return;
165                                         }
166                                 }
167                                 synfig::warning("Time(): Bad time format");
168                                 break;
169
170                         default:
171                                 value_+=amount;
172                                 synfig::warning("Time(): Unexpected character '%c' when parsing time string \"%s\"",str[pos],str.c_str());
173                                 break;
174                 }
175                 pos++;
176                 amount=0;
177         }
178 }
179
180 String
181 Time::get_string(float fps, Time::Format format)const
182 {
183         Time time(*this);
184
185         if(time<=begin())
186                 return "SOT";   // Start Of Time
187         if(time>=end())
188                 return "EOT";   // End Of Time
189
190         if(fps<0)fps=0;
191
192         if(ceil(time.value_)-time.value_<epsilon_())
193                 time.value_=ceil(time.value_);
194
195         int hour = 0, minute = 0;
196         if(!(format<=FORMAT_FRAMES))
197         {
198                 hour=time/3600;time-=hour*3600;
199                 minute=time/60;time-=minute*60;
200         }
201         // <= is redefined, so this means "is the FORMAT_VIDEO bit set in the format?"
202         if(format<=FORMAT_VIDEO)
203         {
204                 int second;
205                 second=time;time-=second;
206
207                 if(fps && fps>1)
208                 {
209                         int frame;
210                         frame=round_to_int(time*fps);
211
212                         return strprintf("%02d:%02d:%02d.%02d",hour,minute,second,frame);
213                 }
214                 else
215                         return strprintf("%02d:%02d:%02d",hour,minute,second);
216         }
217
218         if (format <= FORMAT_FRAMES)
219         {
220                 if (fps && fps>0)
221                         return strprintf("%df", round_to_int(time * fps));
222                 else
223                         return strprintf("%ds", round_to_int(time * 1));
224         }
225
226         String ret;
227         bool started = false;
228
229         if(format<=FORMAT_FULL || hour)
230         {
231                 ret+=strprintf("%dh",hour);
232                 started = true;
233         }
234
235         if(format<=FORMAT_FULL || minute)
236         {
237                 if (!(format<=FORMAT_NOSPACES) && started)
238                         ret += " ";
239
240                 ret += strprintf("%dm", minute);
241                 started = true;
242         }
243
244         if(fps && fps>1)
245         {
246                 int second;
247                 float frame;
248                 second=time;time-=second;
249                 frame=time*fps;
250
251                 if(format<=FORMAT_FULL || second)
252                 {
253                         if (!(format<=FORMAT_NOSPACES) && started)
254                                 ret += " ";
255
256                         ret += strprintf("%ds", (int)second);
257                         started = true;
258                 }
259
260                 if(format<=FORMAT_FULL || abs(frame) > epsilon_() || !started)
261                 {
262                         if (!(format<=FORMAT_NOSPACES) && started)
263                                 ret += " ";
264
265                         if(abs(frame-floor(frame) >= epsilon_()))
266                                 ret += strprintf("%0.3ff", frame);
267                         else
268                                 ret += strprintf("%0.0ff", frame);
269                 }
270         }
271         else
272         {
273                 float second;
274                 second=time;
275                 if(format<=FORMAT_FULL || second || !started)
276                 {
277                         if (!(format<=FORMAT_NOSPACES) && started)
278                                 ret += " ";
279
280                         if(abs(second-floor(second))>=epsilon_())
281                         {
282                                 String seconds(strprintf("%0.8f",second));
283
284                                 // skip trailing zeros
285                                 int count = 0;
286                                 String::reverse_iterator i = seconds.rbegin();
287                                 for ( ; (*i) == '0'; i++)
288                                         count++;
289
290                                 // if we removed too many, go back one place, leaving one zero
291                                 if (*i < '0' || *i > '9') count--;
292
293                                 ret += seconds.substr(0, seconds.size()-count) + "s";
294                         }
295                         else
296                                 ret+=strprintf("%0.0fs",second);
297                 }
298         }
299
300         return ret;
301 }
302
303 Time
304 Time::round(float fps)const
305 {
306         assert(fps>0);
307
308         value_type time(*this);
309
310         time*=fps;
311
312         if(abs(time-floor(time))<0.5)
313                 return floor(time)/fps;
314         else
315                 return ceil(time)/fps;
316 }
317
318 #ifdef _DEBUG
319 const char *
320 Time::c_str()const
321 {
322         return get_string().c_str();
323 }
324 #endif
325
326 //! \writeme
327 bool
328 Time::is_valid()const
329 {
330         return !isnan(value_);
331 }