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:
parent
62b6394bdf
commit
9712483a75
|
@ -23,10 +23,6 @@ Shape relax(Shape shape) {
|
||||||
return relaxedShapes[static_cast<size_t>(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) {
|
Shape getClosestShape(Shape reference, ShapeSet shapes) {
|
||||||
if (shapes.empty()) {
|
if (shapes.empty()) {
|
||||||
throw std::invalid_argument("Cannot select from empty set of shapes.");
|
throw std::invalid_argument("Cannot select from empty set of shapes.");
|
||||||
|
|
|
@ -11,9 +11,6 @@ Shape getBasicShape(Shape shape);
|
||||||
// Returns the mouth shape that results from relaxing the specified shape.
|
// Returns the mouth shape that results from relaxing the specified shape.
|
||||||
Shape relax(Shape 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.
|
// 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.
|
// The actual selection will be performed based on similarity with the previous or next shape.
|
||||||
using ShapeSet = std::set<Shape>;
|
using ShapeSet = std::set<Shape>;
|
||||||
|
|
|
@ -1,31 +1,40 @@
|
||||||
#include "pauseAnimation.h"
|
#include "pauseAnimation.h"
|
||||||
#include "animationRules.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> animatePauses(const JoiningContinuousTimeline<Shape>& shapes) {
|
||||||
JoiningContinuousTimeline<Shape> result(shapes);
|
JoiningContinuousTimeline<Shape> result(shapes);
|
||||||
|
|
||||||
// Don't close mouth for short pauses
|
for_each_adjacent(shapes.begin(), shapes.end(), [&](const Timed<Shape>& previous, const Timed<Shape>& pause, const Timed<Shape>& next) {
|
||||||
for_each_adjacent(shapes.begin(), shapes.end(), [&](const Timed<Shape>& lhs, const Timed<Shape>& pause, const Timed<Shape>& rhs) {
|
|
||||||
if (pause.getValue() != Shape::X) return;
|
if (pause.getValue() != Shape::X) return;
|
||||||
|
|
||||||
const centiseconds maxPausedOpenMouthDuration = 35_cs;
|
result.set(pause.getTimeRange(), getPauseShape(previous.getValue(), next.getValue(), pause.getDuration()));
|
||||||
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));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
Loading…
Reference in New Issue