File indexing completed on 2024-09-08 03:44:34
0001 /* 0002 This file is part of the KDE games kwin4 program 0003 SPDX-FileCopyrightText: 2006 Martin Heni <kde@heni-online.de> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "introsprite.h" 0009 0010 // own 0011 #include "kfourinline_debug.h" 0012 // Std 0013 #include <cmath> 0014 0015 #define sign(x) ((x) > 0 ? 1 : ((x) < 0 ? -1 : 0)) 0016 0017 /** 0018 * Store an animation script command. The command represents certain possible 0019 * animation sub mentods. The possible methods are listed in the enum Cmd 0020 */ 0021 class AnimationCommand 0022 { 0023 public: 0024 /** 0025 * The supported commands. 0026 */ 0027 enum Cmd { SHOW, HIDE, PAUSE, POSITION, LINEAR_DURATION, CIRCLE_DURATION, MANHATTEN }; 0028 /** 0029 * Construct an animation command 0030 */ 0031 AnimationCommand() 0032 { 0033 cmd = PAUSE; 0034 duration = 0; 0035 radius = 0.0; 0036 velocity = 0.0; 0037 } 0038 /** 0039 * The command type. 0040 */ 0041 Cmd cmd; 0042 /** 0043 * How long does the command take [ms]. 0044 */ 0045 int duration; 0046 /** 0047 * The start position [0..1, 0..1]. 0048 */ 0049 QPointF start; 0050 /** 0051 * The end position [0..1, 0..1]. 0052 */ 0053 QPointF end; 0054 /** 0055 * The radius [0..1] (if necessary). 0056 */ 0057 double radius; 0058 /** 0059 * The velocity [0...1000] (if necessary). 0060 */ 0061 double velocity; 0062 }; 0063 0064 // Constructor for the sprite 0065 IntroSprite::IntroSprite(const QString &id, ThemeManager *theme, int no, QGraphicsScene *scene) 0066 : Themeable(id, theme) 0067 , PixmapSprite(no, scene) 0068 { 0069 hide(); 0070 mDeltaT = 0; 0071 mDelayT = 0; 0072 mNo = no; 0073 mTime.restart(); 0074 mDebugTime.restart(); 0075 mStartAnimation = true; 0076 if (theme) 0077 theme->updateTheme(this); 0078 } 0079 0080 // Destructor 0081 IntroSprite::~IntroSprite() 0082 { 0083 // Clear animation list (deletes objects) 0084 clearAnimation(); 0085 } 0086 0087 // Change the theme 0088 void IntroSprite::changeTheme() 0089 { 0090 PixmapSprite::changeTheme(); 0091 } 0092 0093 // Clear the animation script 0094 void IntroSprite::clearAnimation(bool restartTime) 0095 { 0096 qDeleteAll(mAnimList); 0097 mAnimList.clear(); 0098 0099 // Reset time? 0100 if (restartTime) { 0101 mTime.restart(); 0102 mDeltaT = 0; 0103 mDelayT = 0; 0104 } 0105 mStartAnimation = true; 0106 } 0107 0108 // Add a show sprite command 0109 AnimationCommand *IntroSprite::addShow() 0110 { 0111 AnimationCommand *cmd = new AnimationCommand(); 0112 cmd->cmd = AnimationCommand::SHOW; 0113 cmd->duration = 0; 0114 cmd->start = previousStart(); 0115 cmd->end = previousEnd(); 0116 mAnimList.append(cmd); 0117 return cmd; 0118 } 0119 0120 // Add a hide sprite command 0121 AnimationCommand *IntroSprite::addHide() 0122 { 0123 AnimationCommand *cmd = new AnimationCommand(); 0124 cmd->cmd = AnimationCommand::HIDE; 0125 cmd->duration = 0; 0126 cmd->start = previousStart(); 0127 cmd->end = previousEnd(); 0128 mAnimList.append(cmd); 0129 return cmd; 0130 } 0131 0132 // Add a relative pause 0133 AnimationCommand *IntroSprite::addPause(int duration) 0134 { 0135 AnimationCommand *cmd = new AnimationCommand(); 0136 cmd->cmd = AnimationCommand::PAUSE; 0137 cmd->duration = duration; 0138 cmd->start = previousStart(); 0139 cmd->end = previousEnd(); 0140 mAnimList.append(cmd); 0141 return cmd; 0142 } 0143 0144 // Add an absolute pause...wait until the given time 0145 AnimationCommand *IntroSprite::addWait(int duration) 0146 { 0147 int currentDuration = animationDuration(); 0148 if (currentDuration < duration) { 0149 AnimationCommand *cmd = new AnimationCommand(); 0150 cmd->cmd = AnimationCommand::PAUSE; 0151 cmd->duration = duration - currentDuration; 0152 cmd->start = previousStart(); 0153 cmd->end = previousEnd(); 0154 mAnimList.append(cmd); 0155 return cmd; 0156 } 0157 return nullptr; 0158 } 0159 0160 // Add a set position command 0161 AnimationCommand *IntroSprite::addPosition(QPointF start) 0162 { 0163 AnimationCommand *cmd = new AnimationCommand(); 0164 cmd->cmd = AnimationCommand::POSITION; 0165 cmd->duration = 0; 0166 cmd->start = start; 0167 cmd->end = start; 0168 mAnimList.append(cmd); 0169 return cmd; 0170 } 0171 0172 // Add a linear movement 0173 AnimationCommand *IntroSprite::addLinear(QPointF start, QPointF end, int duration) 0174 { 0175 AnimationCommand *cmd = new AnimationCommand(); 0176 cmd->cmd = AnimationCommand::LINEAR_DURATION; 0177 cmd->duration = duration; 0178 cmd->start = start; 0179 cmd->end = end; 0180 mAnimList.append(cmd); 0181 return cmd; 0182 } 0183 0184 // Add a relative linear movement, that is, starting from the previous position 0185 AnimationCommand *IntroSprite::addRelativeLinear(QPointF end, int duration) 0186 { 0187 AnimationCommand *cmd = new AnimationCommand(); 0188 cmd->cmd = AnimationCommand::LINEAR_DURATION; 0189 cmd->duration = duration; 0190 cmd->start = previousEnd(); 0191 cmd->end = end; 0192 mAnimList.append(cmd); 0193 return cmd; 0194 } 0195 0196 // Add a circle movement 0197 AnimationCommand *IntroSprite::addCircle(QPointF start, double radius, int duration) 0198 { 0199 AnimationCommand *cmd = new AnimationCommand(); 0200 cmd->cmd = AnimationCommand::CIRCLE_DURATION; 0201 cmd->duration = duration; 0202 cmd->start = start; 0203 cmd->end = start; 0204 cmd->radius = radius; 0205 mAnimList.append(cmd); 0206 return cmd; 0207 } 0208 0209 // Add a relative manhatten move 0210 AnimationCommand *IntroSprite::addRelativeManhatten(QPointF end, double velocity) 0211 { 0212 AnimationCommand *cmd = new AnimationCommand(); 0213 cmd->cmd = AnimationCommand::MANHATTEN; 0214 cmd->start = previousEnd(); 0215 cmd->end = end; 0216 cmd->velocity = velocity; 0217 qreal dtx = fabs(cmd->end.x() - cmd->start.x()) / cmd->velocity * 1000.0; 0218 qreal dty = fabs(cmd->end.y() - cmd->start.y()) / cmd->velocity * 1000.0; 0219 cmd->duration = int(dtx + dty + 1.0); 0220 mAnimList.append(cmd); 0221 return cmd; 0222 } 0223 0224 // Retrieve the last commands start position or the current position if none exists 0225 QPointF IntroSprite::previousStart() 0226 { 0227 AnimationCommand *previous = mAnimList.last(); 0228 if (previous != nullptr) 0229 return previous->start; 0230 else 0231 return QPointF(x() / getScale(), y() / getScale()); 0232 } 0233 0234 // Retrieve the last commands end position or the current position if none exists 0235 QPointF IntroSprite::previousEnd() 0236 { 0237 AnimationCommand *previous = mAnimList.last(); 0238 if (previous != nullptr) 0239 return previous->end; 0240 return QPointF(x() / getScale(), y() / getScale()); 0241 } 0242 0243 // Retrieve the duration of the given animation command 0244 int IntroSprite::duration(AnimationCommand *anim) 0245 { 0246 return anim->duration; 0247 } 0248 0249 // Retrieve the overall duration of the animation 0250 int IntroSprite::animationDuration() 0251 { 0252 int dura = 0; 0253 for (int i = 0; i < mAnimList.size(); ++i) { 0254 dura += mAnimList[i]->duration; 0255 } 0256 return dura; 0257 } 0258 0259 // Globally delay the animation 0260 void IntroSprite::delayAnimation(int duration) 0261 { 0262 mDelayT += duration; 0263 } 0264 0265 // CanvasItem advance method 0266 void IntroSprite::advance(int phase) 0267 { 0268 // Ignore phase 0 (collisions) 0269 if (phase == 0) { 0270 QGraphicsItem::advance(phase); 0271 return; 0272 } 0273 0274 // No animation pending 0275 if (mAnimList.isEmpty()) { 0276 QGraphicsItem::advance(phase); 0277 return; 0278 } 0279 0280 // First time animation start 0281 if (mStartAnimation) { 0282 mTime.restart(); 0283 mStartAnimation = false; 0284 } 0285 int elapsed = mTime.elapsed() + mDeltaT - mDelayT; 0286 if (elapsed < 0) 0287 elapsed = 0; 0288 0289 // Get the current/first command 0290 AnimationCommand *anim = mAnimList.first(); 0291 0292 // Execute commands who were passed in the time since the 0293 // last call 0294 while (anim != nullptr && anim->duration <= elapsed) { 0295 executeCmd(anim, anim->duration); 0296 mAnimList.removeFirst(); 0297 elapsed -= anim->duration; 0298 delete anim; 0299 if (!mAnimList.isEmpty()) 0300 anim = mAnimList.first(); 0301 else 0302 anim = nullptr; 0303 } 0304 0305 // Handle current command 0306 if (anim != nullptr) { 0307 executeCmd(anim, elapsed); 0308 } 0309 0310 // Restart timer and store remaining time 0311 mTime.restart(); 0312 mDeltaT = elapsed; 0313 mDelayT = 0; 0314 0315 // Parent advance 0316 QGraphicsItem::advance(phase); 0317 } 0318 0319 // Execute the given animation command. The given time is the time elapsed 0320 // since start of the animation sequence. 0321 void IntroSprite::executeCmd(AnimationCommand *anim, int elapsed) 0322 { 0323 if (anim == nullptr) 0324 return; 0325 0326 // Scale 0327 double scale = this->getScale(); 0328 0329 // Pause command 0330 if (anim->cmd == AnimationCommand::PAUSE) { 0331 // Do nothing 0332 } 0333 // Show sprite command 0334 if (anim->cmd == AnimationCommand::SHOW) { 0335 show(); 0336 } 0337 // Hide sprite command 0338 if (anim->cmd == AnimationCommand::HIDE) { 0339 hide(); 0340 } 0341 // Set position command 0342 if (anim->cmd == AnimationCommand::POSITION) { 0343 qreal x = anim->end.x(); 0344 qreal y = anim->end.y(); 0345 setPos(x * scale, y * scale); 0346 } 0347 // Linear move command 0348 if (anim->cmd == AnimationCommand::LINEAR_DURATION) { 0349 double qt = double(elapsed) / double(anim->duration); 0350 qreal x = anim->start.x() + qt * (anim->end.x() - anim->start.x()); 0351 qreal y = anim->start.y() + qt * (anim->end.y() - anim->start.y()); 0352 setPos(x * scale, y * scale); 0353 } 0354 // Circle move command 0355 if (anim->cmd == AnimationCommand::CIRCLE_DURATION) { 0356 double qt = double(elapsed) / double(anim->duration); 0357 double sign = 1.0; 0358 if (anim->start.x() > 0.5) 0359 sign = -1.0; // Direction of turn 0360 qreal cx = anim->start.x(); 0361 qreal cy = anim->start.y(); 0362 qreal x = cx + anim->radius * sin(sign * qt * 2.0 * M_PI); 0363 qreal y = cy - anim->radius + anim->radius * cos(sign * qt * 2.0 * M_PI); 0364 setPos(x * scale, y * scale); 0365 } 0366 // Manhatten move command 0367 if (anim->cmd == AnimationCommand::MANHATTEN) { 0368 if ((fabs(anim->end.x() - x() / scale) <= 0.00001 && fabs(anim->end.y() - y() / scale) <= 0.00001) || (elapsed >= anim->duration)) { 0369 setPos(anim->end.x() * scale, anim->end.y() * scale); 0370 } 0371 // x-edge 0372 else if (fabs(anim->end.x() - x() / scale) > 0.00001) { 0373 qreal x = anim->start.x() + sign(anim->end.x() - anim->start.x()) * anim->velocity * double(elapsed) / 1000.0; 0374 // Finished? 0375 if ((anim->end.x() > anim->start.x() && x >= anim->end.x()) || (anim->end.x() < anim->start.x() && x <= anim->end.x())) { 0376 x = anim->end.x(); 0377 } 0378 setPos(x * scale, anim->start.y() * scale); 0379 } 0380 // y-edge 0381 else { 0382 qreal dtx = fabs(anim->end.x() - anim->start.x()) / anim->velocity * 1000.0; 0383 qreal y = anim->start.y() + sign(anim->end.y() - anim->start.y()) * anim->velocity * double(elapsed - dtx) / 1000.0; 0384 // Finished? 0385 if ((anim->end.y() > anim->start.y() && y >= anim->end.y()) || (anim->end.y() < anim->start.y() && y <= anim->end.y())) { 0386 y = anim->end.y(); 0387 } 0388 setPos(anim->end.x() * scale, y * scale); 0389 } 0390 } 0391 }