Improved pause animations

Previously, the pause shape could be identical to the following shape,
which meant that the mouth kept frozen at the beginning of the next word.
This commit is contained in:
Daniel Wolf 2016-12-21 20:57:36 +01:00
parent 62b6394bdf
commit 9712483a75
3 changed files with 30 additions and 28 deletions

View File

@ -23,10 +23,6 @@ Shape relax(Shape shape) {
return relaxedShapes[static_cast<size_t>(shape)];
}
Shape getRelaxedBridge(Shape lhs, Shape rhs) {
return lhs == rhs ? lhs : relax(lhs);
}
Shape getClosestShape(Shape reference, ShapeSet shapes) {
if (shapes.empty()) {
throw std::invalid_argument("Cannot select from empty set of shapes.");

View File

@ -11,9 +11,6 @@ Shape getBasicShape(Shape shape);
// Returns the mouth shape that results from relaxing the specified shape.
Shape relax(Shape shape);
// Returns the mouth shape to use for *short* pauses between words.
Shape getRelaxedBridge(Shape lhs, Shape rhs);
// A set of mouth shapes that can be used to represent a certain sound.
// The actual selection will be performed based on similarity with the previous or next shape.
using ShapeSet = std::set<Shape>;

View File

@ -1,31 +1,40 @@
#include "pauseAnimation.h"
#include "animationRules.h"
Shape getPauseShape(Shape previous, Shape next, centiseconds duration) {
// For very short pauses: Just hold the previous shape
if (duration < 12_cs) {
return previous;
}
// For short pauses: Relax the mouth
if (duration <= 35_cs) {
// It looks odd if the pause shape is identical to the next shape.
// Make sure we find a relaxed shape that's different from the next one.
for (Shape currentRelaxedShape = previous;;) {
Shape nextRelaxedShape = relax(currentRelaxedShape);
if (nextRelaxedShape != next) {
return nextRelaxedShape;
}
if (nextRelaxedShape == currentRelaxedShape) {
// We're going in circles
break;
}
currentRelaxedShape = nextRelaxedShape;
}
}
// For longer pauses: Close the mouth
return Shape::X;
}
JoiningContinuousTimeline<Shape> animatePauses(const JoiningContinuousTimeline<Shape>& shapes) {
JoiningContinuousTimeline<Shape> result(shapes);
// Don't close mouth for short pauses
for_each_adjacent(shapes.begin(), shapes.end(), [&](const Timed<Shape>& lhs, const Timed<Shape>& pause, const Timed<Shape>& rhs) {
for_each_adjacent(shapes.begin(), shapes.end(), [&](const Timed<Shape>& previous, const Timed<Shape>& pause, const Timed<Shape>& next) {
if (pause.getValue() != Shape::X) return;
const centiseconds maxPausedOpenMouthDuration = 35_cs;
const TimeRange timeRange = pause.getTimeRange();
if (timeRange.getDuration() <= maxPausedOpenMouthDuration) {
result.set(timeRange, getRelaxedBridge(lhs.getValue(), rhs.getValue()));
}
});
// Keep mouth open into pause if it just opened
for_each_adjacent(shapes.begin(), shapes.end(), [&](const Timed<Shape>& secondLast, const Timed<Shape>& last, const Timed<Shape>& pause) {
if (pause.getValue() != Shape::X) return;
centiseconds lastDuration = last.getDuration();
const centiseconds minOpenDuration = 20_cs;
if (isClosed(secondLast.getValue()) && !isClosed(last.getValue()) && lastDuration < minOpenDuration) {
const centiseconds minSpillDuration = 20_cs;
centiseconds spillDuration = std::min(minSpillDuration, pause.getDuration());
result.set(pause.getStart(), pause.getStart() + spillDuration, getRelaxedBridge(last.getValue(), Shape::X));
}
result.set(pause.getTimeRange(), getPauseShape(previous.getValue(), next.getValue(), pause.getDuration()));
});
return result;