Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/core/Animators/SmartPath/smartpathcollection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ void SmartPathCollection::savePathsSVG(SvgExporter& exp,
if(!forceDumbIncrement && ca_getNumberOfChildren() == 1) {
const auto path0 = getChild(0);
path0->graph_saveSVG(exp, parent, visRange, "d",
[path0, &applier](const int relFrame) {
[path0, &applier](const qreal relFrame) {
auto path = path0->getPathAtRelFrame(relFrame);
if(applier) applier(relFrame, path);
SkString pathStr;
Expand All @@ -86,7 +86,7 @@ void SmartPathCollection::savePathsSVG(SvgExporter& exp,
});
} else {
Animator::saveSVG(exp, parent, visRange, "d",
[this, &applier](const int relFrame) {
[this, &applier](const qreal relFrame) {
auto path = getPathAtRelFrame(relFrame);
if(applier) applier(relFrame, path);
SkString pathStr;
Expand Down
2 changes: 1 addition & 1 deletion src/core/Animators/SmartPath/smartpathcollection.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class CORE_EXPORT SmartPathCollection : public SmartPathCollectionBase {
void prp_writeProperty_impl(eWriteStream& dst) const;
void prp_readProperty_impl(eReadStream& src);

using EffectApplier = std::function<void(const int relFrame,
using EffectApplier = std::function<void(const qreal relFrame,
SkPath& path)>;
void savePathsSVG(SvgExporter& exp,
QDomElement& parent,
Expand Down
45 changes: 45 additions & 0 deletions src/core/Animators/animator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,51 @@ void Animator::saveSVG(SvgExporter& exp,
const QList<Animator*> extInfl) const {
Q_ASSERT(!transform || attrName == "transform");

if(exp.hasFrameMapping()) {
const int span = exp.fAbsRange.span();
if(span <= 1) {
auto value = valueGetter(exp.mapRelFrame(visRange.fMin));
if(transform) {
value = parent.attribute(attrName) + " " +
type + "(" + value + ")";
}
parent.setAttribute(attrName, value.trimmed());
return;
}

const auto tagName = transform ? "animateTransform" : "animate";
auto anim = exp.createElement(tagName);
anim.setAttribute("calcMode", exp.forceDiscreteMapping() ? "discrete" : interpolation);
anim.setAttribute("attributeName", attrName);
if(!type.isEmpty()) anim.setAttribute("type", type);
const qreal div = span - 1;
const qreal dur = div/exp.fFps;
anim.setAttribute("dur", QString::number(dur) + 's');

QStringList values;
QStringList keyTimes;
for(int i = visRange.fMin; i <= visRange.fMax; i++) {
const qreal mapped = exp.mapRelFrame(i);
values << valueGetter(mapped);
const qreal t = (i - exp.fAbsRange.fMin)/div;
keyTimes << QString::number(t);
}
if(keyTimes.isEmpty()) return;
if(keyTimes.last() != "1") {
values << values.last();
keyTimes << "1";
}
if(keyTimes.first() != "0") {
values.prepend(values.first());
keyTimes.prepend("0");
}
anim.setAttribute("values", values.join(';'));
anim.setAttribute("keyTimes", keyTimes.join(';'));
SvgExportHelpers::assignLoop(anim, exp.fLoop);
parent.appendChild(anim);
return;
}

const auto thisIdRange = prp_getIdenticalRelRange(visRange.fMin);
auto idRange = thisIdRange;
for(const auto& infl : extInfl) {
Expand Down
2 changes: 1 addition & 1 deletion src/core/Animators/animator.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ class CORE_EXPORT Animator : public Property {
void anim_appendKeyAction(const stdsptr<Key> &newKey);
void anim_removeKeyAction(const stdsptr<Key> newKey);

using ValueGetter = std::function<QString(const int relFrame)>;
using ValueGetter = std::function<QString(const qreal relFrame)>;
void saveSVG(SvgExporter& exp,
QDomElement& parent,
const FrameRange& visRange,
Expand Down
2 changes: 1 addition & 1 deletion src/core/Animators/coloranimator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ void ColorAnimator::saveColorSVG(SvgExporter &exp,
{
qDebug() << "save color for SVG" << name;
Animator::saveSVG(exp, parent, visRange, name,
[this, &rgba, &a, &exp, &name, &parent](const int relFrame) {
[this, &rgba, &a, &exp, &name, &parent](const qreal relFrame) {
const auto color = getColor(relFrame);
if (exp.fColors11) {
if (color.alphaF() != 1. && (name == "fill" || name == "stroke" || name == "stop-color")) {
Expand Down
64 changes: 64 additions & 0 deletions src/core/Animators/graphanimator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,70 @@ void GraphAnimator::graph_saveSVG(SvgExporter& exp,
const QString &motionPath) const
{
Q_ASSERT(!transform || attrName == "transform");
if(exp.hasFrameMapping()) {
const int span = exp.fAbsRange.span();
if(span <= 1) {
if (motion) { return; }
auto value = valueGetter(exp.mapRelFrame(visRange.fMin));
if (transform) {
value = parent.attribute(attrName) + " " + type + "(" + value + ")";
}
parent.setAttribute(attrName, value.trimmed());
return;
}

const auto tagName = motion ? "animateMotion" :
transform ? "animateTransform" : "animate";
auto anim = exp.createElement(tagName);

if (!beginEvent.isEmpty()) { anim.setAttribute("begin", beginEvent); }
if (!endEvent.isEmpty()) { anim.setAttribute("end", endEvent); }

if (!motion) {
anim.setAttribute("attributeName", attrName);
if (!type.isEmpty()) { anim.setAttribute("type", type); }
} else {
if (motionRotate) { anim.setAttribute("rotate", "auto"); }
if (!motionPath.isEmpty()) {
auto mpath = exp.createElement("mpath");
mpath.setAttribute("href", QString("#%1").arg(AppSupport::filterId(motionPath)));
anim.appendChild(mpath);
}
}

const qreal div = span - 1;
const qreal dur = div/exp.fFps;
anim.setAttribute("dur", QString::number(dur) + 's');

QStringList values;
QStringList keyTimes;
for(int i = visRange.fMin; i <= visRange.fMax; i++) {
const qreal mapped = exp.mapRelFrame(i);
values << valueGetter(mapped);
const qreal t = (i - exp.fAbsRange.fMin)/div;
keyTimes << QString::number(t);
}
if(keyTimes.isEmpty()) return;
if(keyTimes.last() != "1") {
values << values.last();
keyTimes << "1";
}
if(keyTimes.first() != "0") {
values.prepend(values.first());
keyTimes.prepend("0");
}

anim.setAttribute("calcMode", exp.forceDiscreteMapping() ? "discrete" : "linear");
anim.setAttribute("values", values.join(';'));
if (motion) {
anim.setAttribute("keyPoints", values.join(';'));
}
anim.setAttribute("keyTimes", keyTimes.join(';'));
SvgExportHelpers::assignLoop(anim, exp.fLoop);
parent.appendChild(anim);
return;
}

const auto relRange = prp_absRangeToRelRange(exp.fAbsRange);
const auto idRange = prp_getIdenticalRelRange(visRange.fMin);
const int span = exp.fAbsRange.span();
Expand Down
2 changes: 1 addition & 1 deletion src/core/Animators/qpointfanimator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ void QPointFAnimator::saveQPointFSVG(SvgExporter& exp,
const bool transform,
const QString& type) const {
Animator::saveSVG(exp, parent, visRange, name,
[this](const int relFrame) {
[this](const qreal relFrame) {
const auto value = getEffectiveValue(relFrame);
return QString::number(value.x()) + " " +
QString::number(value.y());
Expand Down
2 changes: 1 addition & 1 deletion src/core/Animators/qrealanimator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -852,7 +852,7 @@ void QrealAnimator::saveQrealSVG(SvgExporter& exp,
setExpression(mExpression.sptr());
} else {
graph_saveSVG(exp, parent, visRange, attrName,
[this, mangler, &templ](const int relFrame) {
[this, mangler, &templ](const qreal relFrame) {
const qreal val = mangler(getEffectiveValue(relFrame));
return templ.arg(val);
}, transform, type, beginEvent, endEvent, motion, motionRotate, motionPath);
Expand Down
34 changes: 33 additions & 1 deletion src/core/Animators/qstringanimator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,40 @@ QDomElement createTextElement(SvgExporter& exp, const QString& text) {
void QStringAnimator::saveSVG(SvgExporter& exp, QDomElement& parent,
const PropSetter& propSetter) const {
const auto relRange = prp_absRangeToRelRange(exp.fAbsRange);
const auto idRange = prp_getIdenticalRelRange(relRange.fMin);
const int span = exp.fAbsRange.span();
if(exp.hasFrameMapping()) {
QString currentValue;
FrameRange currentRange{FrameRange::EMIN, FrameRange::EMIN};
for(int i = relRange.fMin; i <= relRange.fMax; i++) {
const qreal mapped = exp.mapRelFrame(i);
const auto value = getValueAtRelFrame(mapped);
if(i == relRange.fMin) {
currentValue = value;
currentRange = {i, i};
} else if(value == currentValue) {
currentRange.fMax = i;
} else {
auto ele = createTextElement(exp, currentValue);
propSetter(ele);
const auto absRange = prp_relRangeToAbsRange(currentRange);
SvgExportHelpers::assignVisibility(exp, ele, exp.fAbsRange*absRange);
parent.appendChild(ele);
currentValue = value;
currentRange = {i, i};
}
}

if(currentRange.fMin != FrameRange::EMIN) {
auto ele = createTextElement(exp, currentValue);
propSetter(ele);
const auto absRange = prp_relRangeToAbsRange(currentRange);
SvgExportHelpers::assignVisibility(exp, ele, exp.fAbsRange*absRange);
parent.appendChild(ele);
}
return;
}

const auto idRange = prp_getIdenticalRelRange(relRange.fMin);
if(idRange.inRange(relRange) || span == 1) {
auto ele = createTextElement(exp, getValueAtRelFrame(relRange.fMin));
propSetter(ele);
Expand Down
2 changes: 2 additions & 0 deletions src/core/Boxes/boundingbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1609,6 +1609,8 @@ eTask* BoundingBox::saveSVGWithTransform(SvgExporter& exp,
const auto expPtr = &exp;
const auto parentPtr = &parent;
taskPtr->addDependent({[ptr, taskPtr, expPtr, parentPtr, visRange, maskId]() {
const SvgExporter::FrameMappingScope mappingScope(*expPtr,
taskPtr->frameMapping());
auto& ele = taskPtr->element();
if (ptr) {
ele.setAttribute("id", AppSupport::filterId(ptr->prp_getName()));
Expand Down
3 changes: 3 additions & 0 deletions src/core/Boxes/containerbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ class GroupSaverSVG : public ComplexTask
, mExp(exp)
, mEle(ele)
, mVisRange(visRange)
, mFrameMapping(exp.currentFrameMapping())
{
// check for masks (DstIn or DstOut)
if (mSrc->isLayer()) {
Expand All @@ -206,6 +207,7 @@ class GroupSaverSVG : public ComplexTask
}

void nextStep() override {
const SvgExporter::FrameMappingScope mappingScope(mExp, mFrameMapping);
if (!mSrc) { return cancel(); }
if (setValue(mI)) { return; }
if (done()) { return; }
Expand All @@ -228,6 +230,7 @@ class GroupSaverSVG : public ComplexTask
QDomElement& mEle;
const FrameRange mVisRange;
QString mItemMaskId;
const SvgExporter::FrameMapping mFrameMapping;

int mI = 0;
};
Expand Down
24 changes: 24 additions & 0 deletions src/core/Boxes/internallinkcanvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "linkcanvasrenderdata.h"
#include "Animators/transformanimator.h"
#include "canvas.h"
#include "svgexporter.h"

InternalLinkCanvas::InternalLinkCanvas(ContainerBox * const linkTarget,
const bool innerLink) :
Expand Down Expand Up @@ -90,13 +91,36 @@ void InternalLinkCanvas::setupRenderData(const qreal relFrame,
}
}

void InternalLinkCanvas::saveSVG(SvgExporter& exp, DomEleTask* const task) const {
const auto linkTarget = getLinkTarget();
if(!linkTarget) return;
if(!mFrameRemapping->enabled()) {
linkTarget->saveSVG(exp, task);
return;
}

SvgExporter::FrameMapping mapping;
const QPointer<const QrealFrameRemapping> remap = mFrameRemapping.get();
mapping.active = true;
mapping.discrete = true;
mapping.mapper = [remap](const qreal frame) -> qreal {
if(!remap) return frame;
return remap->frame(frame);
};

const SvgExporter::FrameMappingScope scope(exp, mapping);
linkTarget->saveSVG(exp, task);
}

bool InternalLinkCanvas::clipToCanvas() {
return mClipToCanvas->getValue();
}

qsptr<BoundingBox> InternalLinkCanvas::createLink(const bool inner) {
auto linkBox = enve::make_shared<InternalLinkCanvas>(this, inner);
copyTransformationTo(linkBox.get());
sWriteReadMember(this, linkBox.get(), &InternalLinkCanvas::mClipToCanvas);
sWriteReadMember(this, linkBox.get(), &InternalLinkCanvas::mFrameRemapping);
return std::move(linkBox);
}

Expand Down
2 changes: 2 additions & 0 deletions src/core/Boxes/internallinkcanvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class CORE_EXPORT InternalLinkCanvas : public InternalLinkGroupBox {
BoxRenderData * const data,
Canvas * const scene);

void saveSVG(SvgExporter& exp, DomEleTask* const task) const override;

qsptr<BoundingBox> createLink(const bool inner);

stdsptr<BoxRenderData> createRenderData();
Expand Down
6 changes: 3 additions & 3 deletions src/core/Boxes/smartvectorpath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ void SmartVectorPath::saveSVG(SvgExporter& exp, DomEleTask* const task) const {
auto fill = exp.createElement("path");
SmartPathCollection::EffectApplier fillApplier;
if(baseEffects || fillEffects) {
fillApplier = [this](const int relFrame, SkPath& path) {
fillApplier = [this](const qreal relFrame, SkPath& path) {
applyBasePathEffects(relFrame, path);
applyFillEffects(relFrame, path);
};
Expand All @@ -93,7 +93,7 @@ void SmartVectorPath::saveSVG(SvgExporter& exp, DomEleTask* const task) const {
auto stroke = exp.createElement("path");
SmartPathCollection::EffectApplier strokeApplier;
if(baseEffects || outlineBaseEffects || outlineEffects) {
strokeApplier = [this, outlineEffects](const int relFrame, SkPath& path) {
strokeApplier = [this, outlineEffects](const qreal relFrame, SkPath& path) {
applyBasePathEffects(relFrame, path);
applyOutlineBaseEffects(relFrame, path);
if(!outlineEffects) return;
Expand Down Expand Up @@ -121,7 +121,7 @@ void SmartVectorPath::saveSVG(SvgExporter& exp, DomEleTask* const task) const {
auto& ele = task->initialize("path");
SmartPathCollection::EffectApplier applier;
if(baseEffects) {
applier = [this](const int relFrame, SkPath& path) {
applier = [this](const qreal relFrame, SkPath& path) {
applyBasePathEffects(relFrame, path);
};
};
Expand Down
Loading