diff --git a/src/core/Animators/transformanimator.cpp b/src/core/Animators/transformanimator.cpp index 21d96460b..c4cd25224 100644 --- a/src/core/Animators/transformanimator.cpp +++ b/src/core/Animators/transformanimator.cpp @@ -423,9 +423,11 @@ void AdvancedTransformAnimator::setPivotFixedTransform( } else if(posAnimated && !pivotAnimated) { mPosAnimator->incAllBaseValues(posXInc, posYInc); mPivotAnimator->setBaseValueWithoutCallingUpdater(newPivot); + mPivotAnimator->prp_afterChangedCurrent(UpdateReason::userChange); } else { // if(!posAnimated && !pivotAnimated) { mPosAnimator->incBaseValuesWithoutCallingUpdater(posXInc, posYInc); mPivotAnimator->setBaseValueWithoutCallingUpdater(newPivot); + mPivotAnimator->prp_afterChangedCurrent(UpdateReason::userChange); } } diff --git a/src/core/Boxes/boundingbox.cpp b/src/core/Boxes/boundingbox.cpp index 4da8ea0fd..d9f35b343 100644 --- a/src/core/Boxes/boundingbox.cpp +++ b/src/core/Boxes/boundingbox.cpp @@ -85,8 +85,14 @@ BoundingBox::BoundingBox(const QString& name, const eBoxType type) : ca_addChild(mTransformAnimator); const auto pivotAnim = mTransformAnimator->getPivotAnimator(); - connect(pivotAnim, &Property::prp_currentFrameChanged, - this, &BoundingBox::requestGlobalPivotUpdateIfSelected); + if(pivotAnim) { + connect(pivotAnim, &Property::prp_currentFrameChanged, + this, [this](const UpdateReason reason) { + requestGlobalPivotUpdateIfSelected(); + planUpdate(reason); + onPivotChanged(reason); + }); + } ca_addChild(mRasterEffectsAnimators); mRasterEffectsAnimators->SWT_hide(); @@ -1036,6 +1042,8 @@ void BoundingBox::setRelativePos(const QPointF &relPos) { mTransformAnimator->setPosition(relPos.x(), relPos.y()); } +void BoundingBox::onPivotChanged(const UpdateReason) {} + void BoundingBox::setOpacity(const qreal opacity) { mTransformAnimator->setOpacity(opacity); } diff --git a/src/core/Boxes/boundingbox.h b/src/core/Boxes/boundingbox.h index 44263cd71..6f7f5d94f 100644 --- a/src/core/Boxes/boundingbox.h +++ b/src/core/Boxes/boundingbox.h @@ -342,6 +342,9 @@ class CORE_EXPORT BoundingBox : public eBoxOrSound { void setPivotAbsPos(const QPointF &absPos); void setPivotRelPos(const QPointF &relPos); +protected: + virtual void onPivotChanged(const UpdateReason reason); +public: bool isAnimated() const; diff --git a/src/core/Boxes/boxwithpatheffects.cpp b/src/core/Boxes/boxwithpatheffects.cpp index e9d118623..aefd37a7a 100644 --- a/src/core/Boxes/boxwithpatheffects.cpp +++ b/src/core/Boxes/boxwithpatheffects.cpp @@ -323,6 +323,14 @@ void BoxWithPathEffects::addOutlineEffects(const qreal relFrame, parent->addOutlineEffects(parentRelFrame, list, influence); } +void BoxWithPathEffects::onPivotChanged(const UpdateReason reason) { + BoundingBox::onPivotChanged(reason); + mPathEffectsAnimators->prp_afterChangedCurrent(reason); + mFillPathEffectsAnimators->prp_afterChangedCurrent(reason); + mOutlineBasePathEffectsAnimators->prp_afterChangedCurrent(reason); + mOutlinePathEffectsAnimators->prp_afterChangedCurrent(reason); +} + void BoxWithPathEffects::getMotionBlurProperties(QList &list) const { BoundingBox::getMotionBlurProperties(list); list.append(mPathEffectsAnimators.get()); diff --git a/src/core/Boxes/boxwithpatheffects.h b/src/core/Boxes/boxwithpatheffects.h index fd7c66754..e1db65004 100644 --- a/src/core/Boxes/boxwithpatheffects.h +++ b/src/core/Boxes/boxwithpatheffects.h @@ -102,6 +102,7 @@ class CORE_EXPORT BoxWithPathEffects : public BoundingBox { PathCallerList& list, const qreal influence = 1) const; protected: + void onPivotChanged(const UpdateReason reason) override; void getMotionBlurProperties(QList &list) const; qsptr mPathEffectsAnimators; diff --git a/src/core/PathEffects/duplicatepatheffect.cpp b/src/core/PathEffects/duplicatepatheffect.cpp index 3dd5d8178..7434b2019 100644 --- a/src/core/PathEffects/duplicatepatheffect.cpp +++ b/src/core/PathEffects/duplicatepatheffect.cpp @@ -26,6 +26,10 @@ #include "duplicatepatheffect.h" #include "Animators/qpointfanimator.h" #include "Animators/intanimator.h" +#include "Animators/qrealanimator.h" +#include "Animators/transformanimator.h" +#include "Boxes/boundingbox.h" +#include "Properties/boolproperty.h" DuplicatePathEffect::DuplicatePathEffect() : PathEffect("duplicate effect", PathEffectType::Duplicate) { @@ -33,26 +37,75 @@ DuplicatePathEffect::DuplicatePathEffect() : mTranslation->setBaseValue(QPointF(10, 10)); ca_addChild(mTranslation); + mRotation = enve::make_shared(0, -360, 360, 1, "rotation"); + ca_addChild(mRotation); + mCount = enve::make_shared(1, 0, 25, 1, "count"); ca_addChild(mCount); + + mUseCustomPivot = enve::make_shared("use custom pivot"); + ca_addChild(mUseCustomPivot); + + mCustomPivot = enve::make_shared("custom pivot"); + mCustomPivot->setBaseValue(QPointF(0, 0)); + ca_addChild(mCustomPivot); } class DuplicateEffectCaller : public PathEffectCaller { public: - DuplicateEffectCaller(const int count, const qreal dX, const qreal dY) : - mCount(count), mDX(toSkScalar(dX)), mDY(toSkScalar(dY)) {} + DuplicateEffectCaller(const int count, + const qreal dX, + const qreal dY, + const qreal rot, + const bool useCustomPivot, + const SkPoint customPivot, + const SkPoint fallbackPivot, + BoxTransformAnimator * const transform, + const qreal relFrame) : + mCount(count), + mDX(toSkScalar(dX)), + mDY(toSkScalar(dY)), + mRot(toSkScalar(rot)), + mUseCustomPivot(useCustomPivot), + mCustomPivot(customPivot), + mFallbackPivot(fallbackPivot), + mTransform(transform), + mRelFrame(relFrame), + mPivotAnimator(transform ? transform->getPivotAnimator() : nullptr) {} void apply(SkPath& path); private: const int mCount; const float mDX; const float mDY; + const float mRot; + const bool mUseCustomPivot; + const SkPoint mCustomPivot; + const SkPoint mFallbackPivot; + BoxTransformAnimator * const mTransform; + const qreal mRelFrame; + QPointFAnimator * const mPivotAnimator; }; void DuplicateEffectCaller::apply(SkPath &path) { + SkPoint pivot; + if(mUseCustomPivot) { + pivot = mCustomPivot; + } else if(mTransform) { + pivot = toSkPoint(mTransform->getPivot(mRelFrame)); + } else if(mPivotAnimator) { + pivot = toSkPoint(mPivotAnimator->getEffectiveValue(mRelFrame)); + } else { + pivot = mFallbackPivot; + } + const SkPath src = path; - for(int i = 1; i <= mCount; i++) - path.addPath(src, i*mDX, i*mDY); + for(int i = 1; i <= mCount; i++) { + SkMatrix m; + m.setTranslate(i*mDX, i*mDY); + m.preRotate(i*mRot, pivot.x(), pivot.y()); + path.addPath(src, m); + } } @@ -61,7 +114,24 @@ stdsptr DuplicatePathEffect::getEffectCaller( const int count = mCount->getEffectiveIntValue(relFrame); const qreal dX = mTranslation->getEffectiveXValue(relFrame)*influence; const qreal dY = mTranslation->getEffectiveYValue(relFrame)*influence; - return enve::make_shared(count, dX, dY); + const qreal rot = mRotation->getEffectiveValue(relFrame)*influence; + const bool useCustomPivot = mUseCustomPivot->getValue(); + const SkPoint customPivot = toSkPoint(mCustomPivot->getEffectiveValue(relFrame)); + SkPoint fallbackPivot = SkPoint::Make(0.f, 0.f); + BoxTransformAnimator *transform = nullptr; + if(const auto owner = getFirstAncestor()) { + fallbackPivot = toSkPoint(owner->getRelBoundingRect().center()); + transform = owner->getBoxTransformAnimator(); + } + return enve::make_shared(count, + dX, + dY, + rot, + useCustomPivot, + customPivot, + fallbackPivot, + transform, + relFrame); } bool DuplicatePathEffect::skipZeroInfluence(const qreal relFrame) const { diff --git a/src/core/PathEffects/duplicatepatheffect.h b/src/core/PathEffects/duplicatepatheffect.h index 37801628d..303fe33b8 100644 --- a/src/core/PathEffects/duplicatepatheffect.h +++ b/src/core/PathEffects/duplicatepatheffect.h @@ -28,6 +28,8 @@ #include "PathEffects/patheffect.h" class IntAnimator; class QPointFAnimator; +class QrealAnimator; +class BoolProperty; class CORE_EXPORT DuplicatePathEffect : public PathEffect { e_OBJECT @@ -35,11 +37,14 @@ class CORE_EXPORT DuplicatePathEffect : public PathEffect { DuplicatePathEffect(); public: stdsptr getEffectCaller( - const qreal relFrame, const qreal influence) const; - bool skipZeroInfluence(const qreal relFrame) const; + const qreal relFrame, const qreal influence) const override; + bool skipZeroInfluence(const qreal relFrame) const override; private: - qsptr mCount; qsptr mTranslation; + qsptr mRotation; + qsptr mCount; + qsptr mUseCustomPivot; + qsptr mCustomPivot; }; #endif // DUPLICATEPATHEFFECT_H