Compare commits

...

4 Commits

Author SHA1 Message Date
Maurycy Liebner
88171af028 Add a color picking button to the paint color widget. 2021-02-27 13:28:23 +01:00
Maurycy Liebner
96b918b8b0 Keep object's position/scale/rotation when changing Transform Effect's
target.
2021-02-27 12:55:13 +01:00
Maurycy Liebner
2d20230608 Disable canvas clipping, use only max bounds. 2021-02-27 11:30:45 +01:00
Maurycy Liebner
96eae885e6 Skip segments with length 0 for outline brush strokes. 2021-02-26 17:42:12 +01:00
17 changed files with 271 additions and 49 deletions

View File

@ -18,6 +18,10 @@
#include "colorvaluerect.h"
#include "colorlabel.h"
#include "colorpickingwidget.h"
#include "GUI/mainwindow.h"
#include "GUI/actionbutton.h"
#include <QWindow>
#include <QVBoxLayout>
@ -75,7 +79,15 @@ PaintColorWidget::PaintColorWidget(QWidget* const parent) :
layout->addWidget(aRect);
layout->addWidget(mColorLabel);
const QString pickIcon = "toolbarButtons/pickUnchecked.png";
const auto pickingButton = new ActionButton(pickIcon, "", this);
connect(pickingButton, &ActionButton::released,
this, &PaintColorWidget::startColorPicking);
const auto colorPickLay = new QHBoxLayout();
colorPickLay->addWidget(mColorLabel);
colorPickLay->addWidget(pickingButton);
layout->addLayout(colorPickLay);
setLayout(layout);
}
@ -111,6 +123,16 @@ void PaintColorWidget::setDisplayedColor(const QColor& color) {
mColorLabel->setAlpha(color.alphaF());
}
void PaintColorWidget::startColorPicking() {
const auto parent = MainWindow::sGetInstance();
const auto screen = parent->windowHandle()->screen();
const auto wid = new ColorPickingWidget(screen, parent);
connect(wid, &ColorPickingWidget::colorSelected,
[this](const QColor & color) {
setColor(color);
});
}
void PaintColorWidget::setColor(const QColor& color) {
setDisplayedColor(color);
emit colorChanged(color);

View File

@ -32,6 +32,7 @@ public:
signals:
void colorChanged(const QColor& color);
private:
void startColorPicking();
void setColor(const QColor& color);
void updateFromRGB();

View File

@ -186,16 +186,8 @@ void BasicTransformAnimator::moveByAbs(const QPointF &absTrans) {
if(!mParentTransform) return;
const auto savedRelPos = mPosAnimator->getSavedValue();
const auto savedAbsPos = mParentTransform->mapRelPosToAbs(savedRelPos);
moveToAbs(savedAbsPos + absTrans);
}
void BasicTransformAnimator::moveToAbs(const QPointF &absPos) {
setAbsolutePos(absPos);
}
void BasicTransformAnimator::setAbsolutePos(const QPointF &pos) {
if(!mParentTransform) return;
setRelativePos(mParentTransform->mapAbsPosToRel(pos));
const auto relPos = mParentTransform->mapAbsPosToRel(savedAbsPos + absTrans);
setRelativePos(relPos);
}
void BasicTransformAnimator::setRelativePos(const QPointF &relPos) {

View File

@ -54,8 +54,6 @@ public:
void startScaleTransform();
void setRelativePos(const QPointF &relPos);
void setAbsolutePos(const QPointF &pos);
void moveToAbs(const QPointF &absPos);
void moveByAbs(const QPointF &absTrans);
void rotateRelativeToSavedValue(const qreal rotRel);

View File

@ -52,8 +52,10 @@ QList<BrushStrokeSet> BrushStrokeSet::sFromSkPath(
if(segLists.isEmpty()) return result;
for(auto& segs : segLists) {
if(segs.isEmpty()) continue;
const double totLen = segs.getTotalLength();
if(isZero4Dec(totLen)) continue;
const double minL = 0;
const double maxL = segs.isClosed() ? 1 + 10/segs.getTotalLength() : 1;
const double maxL = segs.isClosed() ? 1 + 10/totLen : 1;
auto segsT = segs.getFragmentUnbound(minL, maxL);
result << sFromCubicList(segsT, timeCurve,
pressureCurve, widthCurve, spacingCurve);

View File

@ -29,6 +29,8 @@ BoundingBox* BoxTargetProperty::getTarget() const {
void BoxTargetProperty::setTargetAction(BoundingBox* const box) {
if(box == mTarget_d) return;
const auto oldValue = mTarget_d.get();
emit setActionStarted(oldValue, box);
{
prp_pushUndoRedoName("Set Box Target");
UndoRedo ur;
@ -44,6 +46,7 @@ void BoxTargetProperty::setTargetAction(BoundingBox* const box) {
}
setTarget(box);
emit setActionFinished(oldValue, box);
}
void BoxTargetProperty::setTarget(BoundingBox* const box) {

View File

@ -53,6 +53,8 @@ public:
void setValidator(const Validator& validator)
{ mValidator = validator; }
signals:
void setActionStarted(BoundingBox*, BoundingBox*);
void setActionFinished(BoundingBox*, BoundingBox*);
void targetSet(BoundingBox*);
private:
std::function<bool(BoundingBox*)> mValidator = nullptr;

View File

@ -34,10 +34,12 @@ CubicList::CubicList(const CubicList &src) {
CubicList CubicList::getFragment(const qreal minLenFrac,
const qreal maxLenFrac) const {
if(minLenFrac > maxLenFrac) return CubicList();
const qreal totLen = getTotalLength();
if(isZero4Dec(totLen)) return *this;
//Q_ASSERT(minLenFrac > 0);
//Q_ASSERT(maxLenFrac < 1);
const qreal minLen = getTotalLength()*CLAMP01(minLenFrac);
const qreal maxLen = getTotalLength()*CLAMP01(maxLenFrac);
const qreal minLen = totLen*CLAMP01(minLenFrac);
const qreal maxLen = totLen*CLAMP01(maxLenFrac);
qreal minT = 0, maxT = 1;
int minI = -1, maxI = mSegments.count() - 1;
qreal currLen = 0;
@ -58,6 +60,7 @@ CubicList CubicList::getFragment(const qreal minLenFrac,
break;
}
}
if(minI == -1) return CubicList();
QList<qCubicSegment2D> fragSegs;
if(minI == maxI) {

View File

@ -38,6 +38,51 @@ FollowObjectTransformEffect::FollowObjectTransformEffect() :
ca_addChild(mRotInfluence);
}
void FollowObjectTransformEffect::setRotScaleAfterTargetChange(
BoundingBox* const oldTarget, BoundingBox* const newTarget) {
const auto parent = getFirstAncestor<BoundingBox>();
if(!parent) return;
const qreal scaleXInfl = mScaleInfluence->getEffectiveXValue();
const qreal scaleYInfl = mScaleInfluence->getEffectiveYValue();
const qreal rotInfl = mRotInfluence->getEffectiveValue();
qreal rot = 0.;
qreal scaleX = 1.;
qreal scaleY = 1.;
if(oldTarget) {
const auto trans = oldTarget->getTransformAnimator();
const auto rotAnim = trans->getRotAnimator();
const auto scaleAnim = trans->getScaleAnimator();
rot += rotAnim->getEffectiveValue()*rotInfl;
scaleX *= 1 + (scaleAnim->getEffectiveXValue() - 1)*scaleXInfl;
scaleY *= 1 + (scaleAnim->getEffectiveYValue() - 1)*scaleYInfl;
}
if(newTarget) {
const auto trans = newTarget->getTransformAnimator();
const auto rotAnim = trans->getRotAnimator();
const auto scaleAnim = trans->getScaleAnimator();
rot -= rotAnim->getEffectiveValue()*rotInfl;
const qreal scaleXDiv = 1 + (scaleAnim->getEffectiveXValue() - 1)*scaleXInfl;
const qreal scaleYDiv = 1 + (scaleAnim->getEffectiveYValue() - 1)*scaleYInfl;
if(!isZero4Dec(scaleXDiv)) {
scaleX /= scaleXDiv;
}
if(!isZero4Dec(scaleYDiv)) {
scaleY /= scaleYDiv;
}
}
parent->startRotTransform();
parent->rotateBy(rot);
parent->startScaleTransform();
parent->scale(scaleX, scaleY);
}
void FollowObjectTransformEffect::applyEffect(
const qreal relFrame,

View File

@ -33,6 +33,11 @@ public:
qreal &shearX, qreal &shearY,
BoundingBox* const parent) override;
private:
void setRotScaleAfterTargetChange(
BoundingBox* const oldTarget,
BoundingBox* const newTarget) override;
qsptr<QPointFAnimator> mPosInfluence;
qsptr<QPointFAnimator> mScaleInfluence;
qsptr<QrealAnimator> mRotInfluence;

View File

@ -35,6 +35,95 @@ FollowPathTransformEffect::FollowPathTransformEffect() :
ca_addChild(mInfluence);
}
void calculateFollowRotPosChange(
const SkPath relPath,
const QMatrix transform,
const bool lengthBased,
const bool rotate,
const qreal infl,
qreal per,
qreal& rotChange,
qreal& posXChange,
qreal& posYChange) {
SkPath path;
relPath.transform(toSkMatrix(transform), &path);
const QPainterPath qpath = toQPainterPath(path);
if(lengthBased) {
const qreal length = qpath.length();
per = qpath.percentAtLength(per*length);
}
const auto p1 = qpath.pointAtPercent(per);
if(rotate) {
qreal t2 = per + 0.0001;
const bool reverse = t2 > 1;
if(reverse) t2 = 0.9999;
const auto p2 = qpath.pointAtPercent(t2);
const QLineF baseLine(QPointF(0., 0.), QPointF(100., 0.));
QLineF l;
if(reverse) l = QLineF(p2, p1);
else l = QLineF(p1, p2);
qreal trackAngle = l.angleTo(baseLine);
if(trackAngle > 180) trackAngle -= 360;
rotChange = trackAngle*infl;
} else rotChange = 0;
posXChange = p1.x();
posYChange = p1.y();
}
void FollowPathTransformEffect::setRotScaleAfterTargetChange(
BoundingBox* const oldTarget, BoundingBox* const newTarget) {
const bool rotate = mRotate->getValue();
if(!rotate) return;
const auto parent = getFirstAncestor<BoundingBox>();
if(!parent) return;
const auto oldTargetP = static_cast<PathBox*>(oldTarget);
const auto newTargetP = static_cast<PathBox*>(newTarget);
const qreal infl = mInfluence->getEffectiveValue();
const qreal per = mComplete->getEffectiveValue();
const bool lengthBased = mLengthBased->getValue();
const qreal relFrame = parent->anim_getCurrentRelFrame();
const auto parentTransform = parent->getInheritedTransformAtFrame(relFrame);
qreal rot = 0.;
if(oldTargetP) {
const auto relPath = oldTargetP->getRelativePath();
const auto targetTransform = oldTargetP->getTotalTransform();
const auto transform = targetTransform*parentTransform.inverted();
qreal rotChange;
qreal posXChange;
qreal posYChange;
calculateFollowRotPosChange(relPath, transform,
lengthBased, rotate, infl, per,
rotChange, posXChange, posYChange);
rot += rotChange;
}
if(newTargetP) {
const auto relPath = newTargetP->getRelativePath();
const auto targetTransform = newTargetP->getTotalTransform();
const auto transform = targetTransform*parentTransform.inverted();
qreal rotChange;
qreal posXChange;
qreal posYChange;
calculateFollowRotPosChange(relPath, transform,
lengthBased, rotate, infl, per,
rotChange, posXChange, posYChange);
rot -= rotChange;
}
parent->startRotTransform();
parent->rotateBy(rot);
}
void FollowPathTransformEffect::applyEffect(
const qreal relFrame,
qreal& pivotX, qreal& pivotY,
@ -63,38 +152,22 @@ void FollowPathTransformEffect::applyEffect(
const auto transform = targetTransform*parentTransform.inverted();
SkPath path;
const auto relPath = target->getRelativePath(targetRelFrame);
relPath.transform(toSkMatrix(transform), &path);
const QPainterPath qpath = toQPainterPath(path);
const qreal infl = mInfluence->getEffectiveValue(relFrame);
qreal per = mComplete->getEffectiveValue(relFrame);
const bool rotate = mRotate->getValue();
const bool lengthBased = mLengthBased->getValue();
if(lengthBased) {
const qreal length = qpath.length();
per = qpath.percentAtLength(per*length);
}
const auto p1 = qpath.pointAtPercent(per);
qreal rotChange;
qreal posXChange;
qreal posYChange;
if(rotate) {
qreal t2 = per + 0.0001;
const bool reverse = t2 > 1;
if(reverse) t2 = 0.9999;
const auto p2 = qpath.pointAtPercent(t2);
calculateFollowRotPosChange(relPath, transform,
lengthBased, rotate, infl, per,
rotChange, posXChange, posYChange);
const QLineF baseLine(QPointF(0., 0.), QPointF(100., 0.));
QLineF l;
if(reverse) l = QLineF(p2, p1);
else l = QLineF(p1, p2);
qreal trackAngle = l.angleTo(baseLine);
if(trackAngle > 180) trackAngle -= 360;
if(rotate) rot += rotChange;
rot += trackAngle*infl;
}
posX += p1.x()*infl;
posY += p1.y()*infl;
posX += posXChange; //p1.x()*infl;
posY += posYChange; //p1.y()*infl;
}

View File

@ -33,6 +33,10 @@ public:
qreal &shearX, qreal &shearY,
BoundingBox* const parent) override;
private:
void setRotScaleAfterTargetChange(
BoundingBox* const oldTarget,
BoundingBox* const newTarget) override;
qsptr<BoolProperty> mRotate;
qsptr<BoolProperty> mLengthBased;
qsptr<QrealAnimator> mComplete;

View File

@ -18,6 +18,8 @@
#include "Boxes/boundingbox.h"
#include "Animators/transformanimator.h"
#include "Animators/qrealanimator.h"
#include "Animators/qpointfanimator.h"
TargetTransformEffect::TargetTransformEffect(
const QString& name,
@ -50,6 +52,28 @@ TargetTransformEffect::TargetTransformEffect(
}
});
connect(mTarget.get(), &BoxTargetProperty::setActionStarted,
this, [this]() {
const auto parent = getFirstAncestor<BoundingBox>();
if(!parent) return;
mPosBeforeTargetChange = parent->getPivotAbsPos();
});
connect(mTarget.get(), &BoxTargetProperty::setActionFinished,
this, [this](BoundingBox* const oldTarget,
BoundingBox* const newTarget) {
const auto parent = getFirstAncestor<BoundingBox>();
if(!parent) return;
const auto posAfter = parent->getPivotAbsPos();
parent->startPosTransform();
parent->moveByAbs(mPosBeforeTargetChange - posAfter);
setRotScaleAfterTargetChange(oldTarget, newTarget);
parent->finishTransform();
});
ca_addChild(mTarget);
}
@ -66,6 +90,12 @@ FrameRange TargetTransformEffect::prp_getIdenticalRelRange(
return thisIdent*prp_absRangeToRelRange(absParentIdent);
}
void TargetTransformEffect::setRotScaleAfterTargetChange(
BoundingBox* const oldTarget, BoundingBox* const newTarget) {
Q_UNUSED(oldTarget)
Q_UNUSED(newTarget)
}
BoxTargetProperty* TargetTransformEffect::targetProperty() const {
return mTarget.get();
}

View File

@ -28,8 +28,13 @@ public:
FrameRange prp_getIdenticalRelRange(const int relFrame) const;
protected:
virtual void setRotScaleAfterTargetChange(
BoundingBox* const oldTarget,
BoundingBox* const newTarget);
BoxTargetProperty* targetProperty() const;
private:
QPointF mPosBeforeTargetChange;
ConnContextQPtr<BoundingBox> mTargetConn;
qsptr<BoxTargetProperty> mTarget;
};

View File

@ -27,6 +27,43 @@ TrackTransformEffect::TrackTransformEffect() :
ca_prependChild(targetProperty(), mInfluence);
}
qreal calculateTrackAngle(const QPointF& parentPos,
const QPointF& targetPos) {
const QLineF baseLine(parentPos, parentPos + QPointF(100., 0.));
const QLineF trackLine(parentPos, targetPos);
qreal trackAngle = trackLine.angleTo(baseLine);
if(trackAngle > 180) trackAngle -= 360;
return trackAngle;
}
void TrackTransformEffect::setRotScaleAfterTargetChange(
BoundingBox* const oldTarget, BoundingBox* const newTarget) {
const auto parent = getFirstAncestor<BoundingBox>();
if(!parent) return;
const qreal infl = mInfluence->getEffectiveValue();
qreal rot = 0.;
if(oldTarget) {
const auto targetPos = oldTarget->getPivotAbsPos();
const auto parentPos = parent->getPivotAbsPos();
const qreal trackAngle = calculateTrackAngle(parentPos, targetPos);
rot += trackAngle*infl;
}
if(newTarget) {
const auto targetPos = newTarget->getPivotAbsPos();
const auto parentPos = parent->getPivotAbsPos();
const qreal trackAngle = calculateTrackAngle(parentPos, targetPos);
rot -= trackAngle*infl;
}
parent->startRotTransform();
parent->rotateBy(rot);
}
void TrackTransformEffect::applyEffect(
const qreal relFrame,
qreal& pivotX, qreal& pivotY,
@ -54,12 +91,7 @@ void TrackTransformEffect::applyEffect(
const auto targetPos = target->getPivotAbsPos(targetRelFrame);
const auto parentPos = parent->getPivotAbsPos(relFrame);
const QLineF baseLine(parentPos, parentPos + QPointF(100., 0.));
const QLineF trackLine(parentPos, targetPos);
qreal trackAngle = trackLine.angleTo(baseLine);
if(trackAngle > 180) trackAngle -= 360;
const qreal infl = mInfluence->getEffectiveValue(relFrame);
const qreal trackAngle = calculateTrackAngle(parentPos, targetPos);
rot += trackAngle*infl;
}

View File

@ -31,6 +31,10 @@ public:
qreal &shearX, qreal &shearY,
BoundingBox* const parent) override;
private:
void setRotScaleAfterTargetChange(
BoundingBox* const oldTarget,
BoundingBox* const newTarget) override;
qsptr<QrealAnimator> mInfluence;
};

View File

@ -305,8 +305,9 @@ public:
}
QRect getCurrentBounds() const {
if(mClipToCanvasSize) return getCanvasBounds();
else return getMaxBounds();
//if(mClipToCanvasSize) return getCanvasBounds();
//else return getMaxBounds();
return getMaxBounds();
}
int getCanvasHeight() const {