1 // Copyright (C) 2012-2019 The VPaint Developers.
2 // See the COPYRIGHT file at the top-level directory of this distribution
3 // and at https://github.com/dalboris/vpaint/blob/master/COPYRIGHT
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 
17 #include <QTextStream>
18 #include <cmath>
19 
20 #include "TimeDef.h"
21 
22 #define FPS 1
23 #define EPSILON 1.0e-10
24 
25 
Time()26 Time::Time() :
27     type_(ExactFrame),
28     frame_(0), time_(0)
29 {
30 }
31 
Time(int f)32 Time::Time(int f) :
33     type_(ExactFrame),
34     frame_(f),
35     time_(f/(double)FPS)
36 {
37 }
38 
Time(int f,bool justAfter)39 Time::Time(int f, bool justAfter) :
40     frame_(f),
41     time_(f/(double)FPS)
42 {
43     if(justAfter)
44     {
45         type_ = JustAfterFrame;
46         // TODO:  change the  implementation, use  the nextafter()
47         // methods, providing in C99 or C++11
48         // e.g.: 1.0e7 + 1.0e-10 != 1.0e7
49         // but 1.0e5 + 1.0e-10 >> 1.0e5 (>> means lot of floats inbetween)
50 
51         // would still be ok in normal cases, and even -1 day < t < 1 day...
52         time_ += EPSILON;
53     }
54     else
55     {
56         type_ = JustBeforeFrame;
57         time_ -= EPSILON;
58     }
59 }
60 
Time(double t)61 Time::Time(double t) :
62     type_(FloatTime),
63     frame_(std::floor(t*FPS)), // by default, truncate towards negative infinite
64     time_(t)
65 {
66     double eps = 1.0e-4;
67     double rounded = std::floor(t*FPS+0.5);
68     double rest = t - rounded;
69     if( -eps<rest && rest<eps ) // unless epsilon-close to an integer (e.g., t=41.9999)
70     {
71         type_ = ExactFrame;     // in which case we assume the frame is exact
72         frame_ = (int) rounded; // and truncate to nearest (e.g., 42)
73     }
74 }
75 
operator <(const Time & other) const76 bool Time::operator<(const Time & other) const
77 {
78     if(type() != other.type())
79     {
80         // a robust way would not do that, but ok in normal cases
81         return floatTime() < other.floatTime();
82     }
83     else
84     {
85         switch (type())
86         {
87         case ExactFrame:
88         case JustBeforeFrame:
89         case JustAfterFrame:
90             return frame() < other.frame();
91         case FloatTime:
92             return floatTime() < other.floatTime();
93         default:
94             return false;
95         }
96     }
97 }
98 
operator >(const Time & other) const99 bool Time::operator>(const Time & other) const
100 {
101     return other<*this;
102 }
103 
operator >=(const Time & other) const104 bool Time::operator>=(const Time & other) const
105 {
106     return (*this > other) || (*this == other);
107 }
108 
operator <=(const Time & other) const109 bool Time::operator<=(const Time & other) const
110 {
111     return (*this < other) || (*this == other);
112 }
113 
operator ==(const Time & other) const114 bool Time::operator==(const Time & other) const
115 {
116     if(type() != other.type())
117         return false;
118     else
119     {
120         switch (type())
121         {
122         case ExactFrame:
123         case JustBeforeFrame:
124         case JustAfterFrame:
125             return frame() == other.frame();
126         case FloatTime:
127             return floatTime() == other.floatTime();
128         default:
129             return true;
130         }
131     }
132 }
133 
operator !=(const Time & other) const134 bool Time::operator!=(const Time & other) const
135 {
136     return !( *this == other );
137 }
138 
operator +(const Time & other) const139 Time Time::operator+(const Time & other) const
140 {
141     if(type() != other.type())
142     {
143         // a robust way would not do that, but ok in normal cases
144         return Time(floatTime() + other.floatTime());
145     }
146     else
147     {
148         switch (type())
149         {
150         case ExactFrame:
151         case JustBeforeFrame:
152         case JustAfterFrame:
153             return Time(frame() + other.frame());
154         case FloatTime:
155             return Time(floatTime() + other.floatTime());
156         default:
157             return Time();
158         }
159     }
160 }
161 
operator -(const Time & other) const162 Time Time::operator-(const Time & other) const
163 {
164     if(type() != other.type())
165     {
166         // a robust way would not do that, but ok in normal cases
167         return Time(floatTime() - other.floatTime());
168     }
169     else
170     {
171         switch (type())
172         {
173         case ExactFrame:
174         case JustBeforeFrame:
175         case JustAfterFrame:
176             return Time(frame() - other.frame());
177         case FloatTime:
178             return Time(floatTime() - other.floatTime());
179         default:
180             return Time();
181         }
182     }
183 }
184 
185 
save(QTextStream & out)186 void Time::save(QTextStream & out)
187 {
188     switch (type())
189     {
190     case ExactFrame:
191         out << "ExactFrame " << frame();
192         return;
193     case JustBeforeFrame:
194         out << "JustBeforeFrame " << frame();
195         return;
196     case JustAfterFrame:
197         out << "JustAfterFrame " << frame();
198         return;
199     case FloatTime:
200         out << "FloatTime " << floatTime();
201         return;
202     default:
203         return;
204     }
205 }
206 
operator <<(QTextStream & str,const Time & time)207 QTextStream & operator<<(QTextStream & str, const Time & time)
208 {
209     switch (time.type())
210     {
211     case Time::ExactFrame:
212         str << "ExactFrame " << time.frame();
213         return str;
214     case Time::JustBeforeFrame:
215         str << "JustBeforeFrame " << time.frame();
216         return str;
217     case Time::JustAfterFrame:
218         str << "JustAfterFrame " << time.frame();
219         return str;
220     case Time::FloatTime:
221         str << "FloatTime " << time.floatTime();
222         return str;
223     default:
224         return str;
225     }
226 }
227 
operator >>(QTextStream & str,Time & time)228 QTextStream & operator>>(QTextStream & str, Time & time)
229 {
230     QString type;
231     str >> type;
232 
233     if(type == "ExactFrame")
234     {
235         int frame;
236         str >> frame;
237         time = Time(frame);
238     }
239     else if(type == "JustBeforeFrame")
240     {
241         int frame;
242         str >> frame;
243         time = Time(frame, 0);
244     }
245     else if(type == "JustAfterFrame")
246     {
247         int frame;
248         str >> frame;
249         time = Time(frame, 1);
250     }
251     else if(type == "FloatTime")
252     {
253         double t;
254         str >> t;
255         time = Time(t);
256     }
257 
258     return str;
259 }
260