rhubarb-lip-sync/rhubarb/src/animation/shape-rule.cpp

82 lines
2.6 KiB
C++

#include "shape-rule.h"
#include <boost/range/adaptor/transformed.hpp>
#include <utility>
#include "time/continuous-timeline.h"
using boost::optional;
using boost::adaptors::transformed;
template <typename T, bool AutoJoin>
ContinuousTimeline<optional<T>, AutoJoin> boundedTimelinetoContinuousOptional(
const BoundedTimeline<T, AutoJoin>& timeline
) {
return {
timeline.getRange(),
boost::none,
timeline | transformed([](const Timed<T>& timedValue) {
return Timed<optional<T>>(timedValue.getTimeRange(), timedValue.getValue());
})
};
}
ShapeRule::ShapeRule(ShapeSet shapeSet, optional<Phone> phone, TimeRange phoneTiming) :
shapeSet(std::move(shapeSet)),
phone(std::move(phone)),
phoneTiming(phoneTiming) {}
ShapeRule ShapeRule::getInvalid() {
return {{}, boost::none, {0_cs, 0_cs}};
}
bool ShapeRule::operator==(const ShapeRule& rhs) const {
return shapeSet == rhs.shapeSet && phone == rhs.phone && phoneTiming == rhs.phoneTiming;
}
bool ShapeRule::operator!=(const ShapeRule& rhs) const {
return !operator==(rhs);
}
bool ShapeRule::operator<(const ShapeRule& rhs) const {
return shapeSet < rhs.shapeSet || phone < rhs.phone
|| phoneTiming.getStart() < rhs.phoneTiming.getStart()
|| phoneTiming.getEnd() < rhs.phoneTiming.getEnd();
}
ContinuousTimeline<ShapeRule> getShapeRules(const BoundedTimeline<Phone>& phones) {
// Convert to continuous timeline so that silences aren't skipped when iterating
auto continuousPhones = boundedTimelinetoContinuousOptional(phones);
// Create timeline of shape rules
ContinuousTimeline<ShapeRule> shapeRules(
phones.getRange(), {{Shape::X}, boost::none, {0_cs, 0_cs}}
);
centiseconds previousDuration = 0_cs;
for (const auto& timedPhone : continuousPhones) {
optional<Phone> phone = timedPhone.getValue();
const centiseconds duration = timedPhone.getDuration();
if (phone) {
// Animate one phone
Timeline<ShapeSet> phoneShapeSets = getShapeSets(*phone, duration, previousDuration);
// Result timing is relative to phone. Make absolute.
phoneShapeSets.shift(timedPhone.getStart());
// Copy to timeline.
// Later shape sets may overwrite earlier ones if overlapping.
for (const auto& timedShapeSet : phoneShapeSets) {
shapeRules.set(
timedShapeSet.getTimeRange(),
ShapeRule(timedShapeSet.getValue(), phone, timedPhone.getTimeRange())
);
}
}
previousDuration = duration;
}
return shapeRules;
}