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