File indexing completed on 2024-06-16 04:16:04

0001 /*
0002  *  SPDX-FileCopyrightText: 2016 Jouni Pentikäinen <joupent@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 
0007 #include "KisAnimCurvesModel.h"
0008 
0009 #include <QAbstractItemModel>
0010 
0011 #include "kis_global.h"
0012 #include "kis_image.h"
0013 #include "kis_node.h"
0014 #include "kis_keyframe_channel.h"
0015 #include "kis_scalar_keyframe_channel.h"
0016 #include "kis_post_execution_undo_adapter.h"
0017 #include "KisAnimUtils.h"
0018 #include "kis_processing_applicator.h"
0019 #include "kis_command_utils.h"
0020 #include "KisImageBarrierLock.h"
0021 
0022 struct KisAnimationCurve::Private
0023 {
0024     Private(KisScalarKeyframeChannel *channel, QColor color)
0025         : channel(channel)
0026         , color(color)
0027         , visible(true)
0028     {}
0029 
0030     KisScalarKeyframeChannel *channel;
0031     QColor color;
0032     bool visible;
0033 };
0034 
0035 KisAnimationCurve::KisAnimationCurve(KisScalarKeyframeChannel *channel, QColor color)
0036     : m_d(new Private(channel, color))
0037 {}
0038 
0039 KisScalarKeyframeChannel *KisAnimationCurve::channel() const
0040 {
0041     return m_d->channel;
0042 }
0043 
0044 QColor KisAnimationCurve::color() const
0045 {
0046     return m_d->color;
0047 }
0048 
0049 void KisAnimationCurve::setVisible(bool visible)
0050 {
0051     m_d->visible = visible;
0052 }
0053 
0054 bool KisAnimationCurve::visible() const
0055 {
0056     return m_d->visible;
0057 }
0058 
0059 struct KisAnimCurvesModel::Private
0060 {
0061     QList<KisAnimationCurve*> curves;
0062     int nextColorHue;
0063     KUndo2Command *undoCommand;
0064 
0065     Private()
0066         : nextColorHue(0)
0067         , undoCommand(0)
0068     {}
0069 
0070     KisAnimationCurve *getCurveAt(const QModelIndex& index) {
0071 
0072         if (!index.isValid()) return 0;
0073 
0074         int row = index.row();
0075 
0076         if (row < 0 || row >= curves.size()) {
0077             return 0;
0078         }
0079 
0080         return curves.at(row);
0081     }
0082 
0083     int rowForCurve(KisAnimationCurve *curve) {
0084         return curves.indexOf(curve);
0085     }
0086 
0087     int rowForChannel(const KisKeyframeChannel *channel) {
0088         for (int row = 0; row < curves.count(); row++) {
0089             if (curves.at(row)->channel() == channel) return row;
0090         }
0091 
0092         return -1;
0093     }
0094 
0095     QColor chooseNextColor() {
0096         if (curves.isEmpty()) nextColorHue = 0;
0097 
0098         QColor color = QColor::fromHsv(nextColorHue, 255, 255);
0099         nextColorHue += 94; // Value chosen experimentally for providing distinct colors
0100         nextColorHue = nextColorHue & 0xff;
0101         return color;
0102     }
0103 };
0104 
0105 KisAnimCurvesModel::KisAnimCurvesModel(QObject *parent)
0106     : KisTimeBasedItemModel(parent)
0107     , m_d(new Private())
0108 {}
0109 
0110 KisAnimCurvesModel::~KisAnimCurvesModel()
0111 {
0112     qDeleteAll(m_d->curves);
0113 }
0114 
0115 int KisAnimCurvesModel::rowCount(const QModelIndex &parent) const
0116 {
0117     Q_UNUSED(parent);
0118     return m_d->curves.size();
0119 }
0120 
0121 QVariant KisAnimCurvesModel::data(const QModelIndex &index, int role) const
0122 {
0123     KisAnimationCurve *curve = m_d->getCurveAt(index);
0124 
0125     if (curve) {
0126         KisScalarKeyframeChannel *channel = curve->channel();
0127         KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(channel, QVariant());
0128 
0129         const int time = index.column();
0130         KisScalarKeyframeSP keyframe = channel->keyframeAt(time).dynamicCast<KisScalarKeyframe>();
0131 
0132         switch (role) {
0133         case SpecialKeyframeExists:
0134             return !keyframe.isNull();
0135         case ScalarValueRole:
0136             return channel->valueAt(time);
0137         case LeftTangentRole: {
0138             if (keyframe.isNull())
0139                 return QVariant();
0140 
0141             const int previousKeyTime = channel->previousKeyframeTime(time);
0142             KisScalarKeyframeSP previousKeyframe = channel->keyframeAt(previousKeyTime).dynamicCast<KisScalarKeyframe>();
0143 
0144             //Tangent null whenever there's no previous keyframe.
0145             if (!previousKeyframe)
0146                 return QVariant();
0147 
0148             //Tangent should be null if previous keyframe not set to bezier.
0149             if (previousKeyframe->interpolationMode() != KisScalarKeyframe::Bezier)
0150                 return QVariant();
0151 
0152             return keyframe->leftTangent();
0153         }
0154         case RightTangentRole: {
0155             if (keyframe.isNull())
0156                 return QVariant();
0157 
0158             if (keyframe->interpolationMode() != KisScalarKeyframe::Bezier)
0159                 return QVariant();
0160 
0161             return keyframe->rightTangent();
0162         }
0163         case InterpolationModeRole:
0164             return (keyframe.isNull()) ? QVariant() : keyframe->interpolationMode();
0165         case TangentsModeRole:
0166             return (keyframe.isNull()) ? QVariant() : keyframe->tangentsMode();
0167         case CurveColorRole:
0168             return curve->color();
0169         case CurveVisibleRole:
0170             return curve->visible();
0171         case PreviousKeyframeTime:
0172         {
0173             int activeKeyframeIndex = channel->activeKeyframeTime(time);
0174             if (!channel->keyframeAt(activeKeyframeIndex)) return QVariant();
0175             if (activeKeyframeIndex < time) {
0176                 return activeKeyframeIndex;
0177             }
0178 
0179             int previousKeyframeIndex = channel->previousKeyframeTime(activeKeyframeIndex);
0180             if (!channel->keyframeAt(previousKeyframeIndex)) return QVariant();
0181             return previousKeyframeIndex;
0182         }
0183         case NextKeyframeTime:
0184         {
0185             int activeKeyframeIndex = channel->activeKeyframeTime(time);
0186             if (!channel->keyframeAt(activeKeyframeIndex)) {
0187                 int firstKeyIndex = channel->firstKeyframeTime();
0188                 if (firstKeyIndex != -1 && firstKeyIndex > time) {
0189                     return firstKeyIndex;
0190                 }
0191                 return QVariant();
0192             }
0193 
0194             int nextKeyframeIndex = channel->nextKeyframeTime(activeKeyframeIndex);
0195             if (!channel->keyframeAt(nextKeyframeIndex)) return QVariant();
0196             return nextKeyframeIndex;
0197         }
0198         case ChannelIdentifier:
0199             return channel->id();
0200         case ChannelLimits:
0201         {
0202             QSharedPointer<ScalarKeyframeLimits> limits = channel->limits();
0203 
0204             if (!limits)
0205                 return QVariant();
0206 
0207             return QVariant::fromValue<ChannelLimitsMetatype>( ChannelLimitsMetatype(limits->lower, limits->upper) );
0208         }
0209         default:
0210             break;
0211         }
0212     }
0213 
0214     return KisTimeBasedItemModel::data(index, role);
0215 }
0216 
0217 bool KisAnimCurvesModel::setData(const QModelIndex &index, const QVariant &value, int role)
0218 {
0219     if (!index.isValid())
0220         return false;
0221 
0222     KisScalarKeyframeChannel *channel = m_d->getCurveAt(index)->channel();
0223     KUndo2Command *command = m_d->undoCommand;
0224 
0225     switch (role) {
0226     case ScalarValueRole:
0227     {
0228         if (channel->keyframeAt(index.column())) {
0229 
0230             if (!command) command = new KUndo2Command(kundo2_i18n("Adjust keyframe"));
0231             channel->keyframeAt<KisScalarKeyframe>(index.column())->setValue(value.toReal(), command);
0232         } else {
0233 
0234             if (!command) command = new KUndo2Command(kundo2_i18n("Insert keyframe"));
0235             channel->addScalarKeyframe(index.column(), value.toReal(), command);
0236         }
0237         dataChanged(index,index);
0238     }
0239         break;
0240     case LeftTangentRole:
0241     case RightTangentRole:
0242     {
0243         KisScalarKeyframeSP keyframe = channel->keyframeAt<KisScalarKeyframe>(index.column());
0244         if (!keyframe) return false;
0245 
0246         QPointF leftTangent = (role == LeftTangentRole ? value.toPointF() : keyframe->leftTangent());
0247         QPointF rightTangent = (role == RightTangentRole ? value.toPointF() : keyframe->rightTangent());
0248 
0249         if (!command) command = new KUndo2Command(kundo2_i18n("Adjust tangent"));
0250         keyframe->setInterpolationTangents(leftTangent, rightTangent, command);
0251         dataChanged(index, index);
0252     }
0253         break;
0254     case InterpolationModeRole:
0255     {
0256         KisScalarKeyframeSP key = channel->keyframeAt<KisScalarKeyframe>(index.column());
0257 
0258         if (!command) command = new KUndo2Command(kundo2_i18n("Set interpolation mode"));
0259         key->setInterpolationMode((KisScalarKeyframe::InterpolationMode)value.toInt(), command);
0260         dataChanged(index, index);
0261     }
0262         break;
0263     case TangentsModeRole:
0264     {
0265         KisScalarKeyframeSP keyframe = channel->keyframeAt<KisScalarKeyframe>(index.column());
0266         if (!keyframe) return false;
0267 
0268         KisScalarKeyframe::TangentsMode mode = (KisScalarKeyframe::TangentsMode)value.toInt();
0269 
0270         if (!command) command = new KUndo2Command(kundo2_i18n("Set interpolation mode"));
0271         keyframe->setTangentsMode( mode, command );
0272         dataChanged(index,index);
0273     }
0274         break;
0275     default:
0276         return KisTimeBasedItemModel::setData(index, value, role);
0277     }
0278 
0279     if (command && !m_d->undoCommand) {
0280         image()->postExecutionUndoAdapter()->addCommand(toQShared(command));
0281     }
0282 
0283     return true;
0284 }
0285 
0286 QVariant KisAnimCurvesModel::headerData(int section, Qt::Orientation orientation, int role) const
0287 {
0288     return KisTimeBasedItemModel::headerData(section, orientation, role);
0289 }
0290 
0291 bool KisAnimCurvesModel::setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role)
0292 {
0293     return KisTimeBasedItemModel::setHeaderData(section, orientation, value, role);
0294 }
0295 
0296 void KisAnimCurvesModel::beginCommand(const KUndo2MagicString &text)
0297 {
0298     KIS_ASSERT_RECOVER_RETURN(!m_d->undoCommand);
0299     m_d->undoCommand = new KUndo2Command(text);
0300 }
0301 
0302 void KisAnimCurvesModel::endCommand()
0303 {
0304     KIS_ASSERT_RECOVER_RETURN(m_d->undoCommand);
0305     image()->postExecutionUndoAdapter()->addCommand(toQShared(m_d->undoCommand));
0306 
0307     m_d->undoCommand = 0;
0308 }
0309 
0310 
0311 bool KisAnimCurvesModel::adjustKeyframes(const QModelIndexList &indexes, int timeOffset, qreal valueOffset)
0312 {
0313     QScopedPointer<KUndo2Command> command(
0314         new KUndo2Command(
0315             kundo2_i18np("Adjust Keyframe",
0316                          "Adjust %1 Keyframes",
0317                          indexes.size())));
0318 
0319     {
0320         KisImageBarrierLock lock(image());
0321 
0322         if (timeOffset != 0) {
0323             bool ok = createOffsetFramesCommand(indexes, QPoint(timeOffset, 0), false, false, command.data());
0324             if (!ok) return false;
0325         }
0326 
0327         using KisAnimUtils::FrameItem;
0328         using KisAnimUtils::FrameItemList;
0329         FrameItemList frameItems;
0330 
0331         Q_FOREACH(QModelIndex index, indexes) {
0332             KisScalarKeyframeChannel *channel = m_d->getCurveAt(index)->channel();
0333             KIS_ASSERT_RECOVER_RETURN_VALUE(channel, false);
0334 
0335             frameItems << FrameItem(channel->node(),
0336                                     channel->id(),
0337                                     index.column() + timeOffset);
0338         };
0339 
0340         new KisCommandUtils::LambdaCommand(
0341             command.data(),
0342             [frameItems, valueOffset] () -> KUndo2Command* {
0343 
0344                 QScopedPointer<KUndo2Command> cmd(new KUndo2Command());
0345 
0346                 bool result = false;
0347 
0348                 Q_FOREACH (const FrameItem &item, frameItems) {
0349                     const int time = item.time;
0350                     KisNodeSP node = item.node;
0351 
0352                     KisKeyframeChannel *channel = node->getKeyframeChannel(item.channel);
0353 
0354                     if (!channel) continue;
0355 
0356                     KisScalarKeyframeSP scalarKeyframe = channel->keyframeAt(time).dynamicCast<KisScalarKeyframe>();
0357 
0358                     if (!scalarKeyframe) continue;
0359 
0360                     const qreal currentValue = scalarKeyframe->value();
0361                     //TODO Undo considerations.
0362                     scalarKeyframe->setValue(currentValue + valueOffset, cmd.data());
0363                     result = true;
0364                 }
0365 
0366                 return result ? new KisCommandUtils::SkipFirstRedoWrapper(cmd.take()) : 0;
0367         });
0368     }
0369 
0370     KisProcessingApplicator::runSingleCommandStroke(image(), command.take(),
0371                                                     KisStrokeJobData::BARRIER,
0372                                                     KisStrokeJobData::EXCLUSIVE);
0373 
0374     return true;
0375 }
0376 
0377 KisAnimationCurve *KisAnimCurvesModel::addCurve(KisScalarKeyframeChannel *channel)
0378 {
0379     beginInsertRows(QModelIndex(), m_d->curves.size(), m_d->curves.size());
0380 
0381     KisAnimationCurve *curve = new KisAnimationCurve(channel, m_d->chooseNextColor());
0382     m_d->curves.append(curve);
0383 
0384     endInsertRows();
0385 
0386     connect(channel, &KisScalarKeyframeChannel::sigAddedKeyframe,
0387             this, &KisAnimCurvesModel::slotKeyframeChanged);
0388 
0389     connect(channel, &KisScalarKeyframeChannel::sigAddedKeyframe,
0390             this, &KisAnimCurvesModel::slotKeyframeAdded);
0391 
0392     connect(channel, &KisScalarKeyframeChannel::sigKeyframeHasBeenRemoved,
0393             this, [this](const KisKeyframeChannel* channel, int time) {
0394         this->slotKeyframeChanged(channel, time);
0395     });
0396 
0397     connect(channel, SIGNAL(sigKeyframeChanged(const KisKeyframeChannel*,int)),
0398             this, SLOT(slotKeyframeChanged(const KisKeyframeChannel*,int)));
0399 
0400     return curve;
0401 }
0402 
0403 void KisAnimCurvesModel::removeCurve(KisAnimationCurve *curve)
0404 {
0405     int index = m_d->curves.indexOf(curve);
0406     if (index < 0) return;
0407 
0408     curve->channel()->disconnect(this);
0409 
0410     beginRemoveRows(QModelIndex(), index, index);
0411 
0412     m_d->curves.removeAt(index);
0413     delete curve;
0414 
0415     endRemoveRows();
0416 }
0417 
0418 void KisAnimCurvesModel::setCurveVisible(KisAnimationCurve *curve, bool visible)
0419 {
0420     curve->setVisible(visible);
0421 
0422     int row = m_d->rowForCurve(curve);
0423     emit dataChanged(index(row, 0), index(row, columnCount()));
0424 }
0425 
0426 KisNodeSP KisAnimCurvesModel::nodeAt(QModelIndex index) const
0427 {
0428     KisAnimationCurve *curve = m_d->getCurveAt(index);
0429     if (curve && curve->channel() && curve->channel()->node()) {
0430         return KisNodeSP(curve->channel()->node());
0431     }
0432     return 0;
0433 }
0434 
0435 QMap<QString, KisKeyframeChannel *> KisAnimCurvesModel::channelsAt(QModelIndex index) const
0436 {
0437     KisKeyframeChannel *channel = m_d->getCurveAt(index)->channel();
0438     QMap<QString, KisKeyframeChannel*> list;
0439     list[""] = channel;
0440     return list;
0441 }
0442 
0443 KisKeyframeChannel *KisAnimCurvesModel::channelByID(QModelIndex index, const QString &id) const
0444 {
0445     return nodeAt(index)->getKeyframeChannel(id);
0446 }
0447 
0448 void KisAnimCurvesModel::slotKeyframeChanged(const KisKeyframeChannel *channel, int time)
0449 {
0450     int row = m_d->rowForChannel(channel);
0451     QModelIndex changedIndex = index(row, time);
0452     emit dataChanged(changedIndex, changedIndex);
0453 }
0454 
0455 void KisAnimCurvesModel::slotKeyframeAdded(const KisKeyframeChannel *channel, int time)
0456 {
0457     emit dataAdded(index(m_d->rowForChannel(channel), time));
0458 }
0459 
0460