File indexing completed on 2025-02-02 04:11:21
0001 /* 0002 * SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #include "follow_path_dialog.hpp" 0008 #include "ui_follow_path_dialog.h" 0009 #include <QEvent> 0010 0011 #include "command/undo_macro_guard.hpp" 0012 #include "command/animation_commands.hpp" 0013 #include "item_models/node_type_proxy_model.hpp" 0014 #include "widgets/dialogs/select_shape_dialog.hpp" 0015 0016 class glaxnimate::gui::FollowPathDialog::Private 0017 { 0018 public: 0019 enum Units 0020 { 0021 Frames, 0022 Seconds 0023 }; 0024 0025 Private(model::AnimatedProperty<QPointF>* property, model::Composition* comp, item_models::DocumentNodeModel* model, FollowPathDialog* parent) 0026 : select_shape_dialog(model, parent), property(property), comp(comp) 0027 { 0028 ui.setupUi(parent); 0029 set_units(Frames); 0030 auto anim = comp->animation.get(); 0031 ui.spin_start->setValue(anim->first_frame.get()); 0032 QSignalBlocker be(ui.spin_end); 0033 QSignalBlocker bd(ui.spin_duration); 0034 ui.spin_end->setValue(anim->last_frame.get()); 0035 ui.spin_duration->setValue(anim->last_frame.get() - anim->first_frame.get()); 0036 } 0037 0038 void set_frames(QDoubleSpinBox* box) 0039 { 0040 box->setSuffix(i18n("f")); 0041 box->setDecimals(0); 0042 auto v = box->value(); 0043 box->setMinimum(comp->animation->first_frame.get()); 0044 box->setMaximum(comp->animation->last_frame.get()); 0045 box->setValue(v * comp->fps.get()); 0046 } 0047 0048 void set_seconds(QDoubleSpinBox* box) 0049 { 0050 auto fps = comp->fps.get(); 0051 box->setSuffix(i18n("\"")); 0052 box->setDecimals(2); 0053 auto v = box->value(); 0054 box->setMinimum(comp->animation->first_frame.get() / fps); 0055 box->setMaximum(comp->animation->last_frame.get() / fps); 0056 box->setValue(v / fps); 0057 } 0058 0059 void set_units(int index) 0060 { 0061 QSignalBlocker be(ui.spin_end); 0062 QSignalBlocker bd(ui.spin_duration); 0063 0064 if ( index == Frames ) 0065 { 0066 set_frames(ui.spin_start); 0067 set_frames(ui.spin_end); 0068 set_frames(ui.spin_duration); 0069 } 0070 else 0071 { 0072 set_seconds(ui.spin_start); 0073 set_seconds(ui.spin_end); 0074 set_seconds(ui.spin_duration); 0075 } 0076 } 0077 0078 model::FrameTime frame(qreal time) 0079 { 0080 if ( ui.combo_units->currentIndex() == Frames ) 0081 return time; 0082 return time * comp->fps.get(); 0083 } 0084 0085 Ui::FollowPathDialog ui; 0086 SelectShapeDialog select_shape_dialog; 0087 model::AnimatedProperty<QPointF>* property; 0088 model::Shape* shape = nullptr; 0089 model::Composition* comp; 0090 }; 0091 0092 glaxnimate::gui::FollowPathDialog::FollowPathDialog(model::AnimatedProperty<QPointF>* property, model::Composition* comp, item_models::DocumentNodeModel* model, QWidget* parent) 0093 : QDialog(parent), d(std::make_unique<Private>(property, comp, model, this)) 0094 {} 0095 0096 glaxnimate::gui::FollowPathDialog::~FollowPathDialog() = default; 0097 0098 void glaxnimate::gui::FollowPathDialog::changeEvent ( QEvent* e ) 0099 { 0100 QDialog::changeEvent(e); 0101 0102 if ( e->type() == QEvent::LanguageChange) 0103 { 0104 d->ui.retranslateUi(this); 0105 } 0106 } 0107 0108 void glaxnimate::gui::FollowPathDialog::apply() 0109 { 0110 if ( !d->shape ) 0111 { 0112 accept(); 0113 return; 0114 } 0115 0116 auto start = d->frame(d->ui.spin_start->value()); 0117 auto end = d->frame(d->ui.spin_end->value()); 0118 auto bezier = d->shape->to_bezier(d->shape->time()); 0119 bezier.add_close_point(); 0120 math::bezier::LengthData length(bezier, 20); 0121 0122 if ( start == end || length.length() == 0 ) 0123 { 0124 accept(); 0125 return; 0126 } 0127 0128 auto guard = command::UndoMacroGuard(i18n("Follow Path"), d->property->object()->document()); 0129 d->property->object()->push_command(new command::RemoveAllKeyframes(d->property, d->property->value())); 0130 0131 for ( int i = 0; i < bezier.size(); i++ ) 0132 { 0133 qreal point_length = i < bezier.size() - 1 ? length.child_start(i) : length.child_end(i-1); 0134 d->property->object()->push_command(new command::SetKeyframe( 0135 d->property, 0136 math::lerp(start, end, point_length / length.length()), 0137 QVariant::fromValue(bezier[i].pos), 0138 true, 0139 true 0140 )); 0141 } 0142 0143 d->property->object()->push_command(new command::SetPositionBezier( 0144 d->property, 0145 bezier, 0146 true 0147 )); 0148 0149 accept(); 0150 } 0151 0152 void glaxnimate::gui::FollowPathDialog::change_duration(double dur) 0153 { 0154 QSignalBlocker be(d->ui.spin_end); 0155 d->ui.spin_end->setValue(d->ui.spin_start->value() + dur); 0156 } 0157 0158 void glaxnimate::gui::FollowPathDialog::change_end(double end) 0159 { 0160 QSignalBlocker bd(d->ui.spin_duration); 0161 d->ui.spin_duration->setValue(end - d->ui.spin_start->value()); 0162 } 0163 0164 void glaxnimate::gui::FollowPathDialog::change_units(int index) 0165 { 0166 d->set_units(index); 0167 } 0168 0169 void glaxnimate::gui::FollowPathDialog::select_path() 0170 { 0171 d->select_shape_dialog.set_shape(d->shape); 0172 if ( d->select_shape_dialog.exec() == QDialog::Accepted ) 0173 { 0174 d->shape = d->select_shape_dialog.shape(); 0175 d->ui.line_shape_name->setText(d->shape ? d->shape->object_name() : ""); 0176 } 0177 } 0178 0179 0180