From 9712483a75eb25deba710e6e6ff1980e6e9ffec0 Mon Sep 17 00:00:00 2001 From: Daniel Wolf Date: Wed, 21 Dec 2016 20:57:36 +0100 Subject: [PATCH] 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. --- src/animation/animationRules.cpp | 4 --- src/animation/animationRules.h | 3 -- src/animation/pauseAnimation.cpp | 51 +++++++++++++++++++------------- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/src/animation/animationRules.cpp b/src/animation/animationRules.cpp index 3f517d2..30e7058 100644 --- a/src/animation/animationRules.cpp +++ b/src/animation/animationRules.cpp @@ -23,10 +23,6 @@ Shape relax(Shape shape) { return relaxedShapes[static_cast(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."); diff --git a/src/animation/animationRules.h b/src/animation/animationRules.h index 1ddb9fb..bd46245 100644 --- a/src/animation/animationRules.h +++ b/src/animation/animationRules.h @@ -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; diff --git a/src/animation/pauseAnimation.cpp b/src/animation/pauseAnimation.cpp index 9f9746c..8e775c2 100644 --- a/src/animation/pauseAnimation.cpp +++ b/src/animation/pauseAnimation.cpp @@ -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 animatePauses(const JoiningContinuousTimeline& shapes) { JoiningContinuousTimeline result(shapes); - - // Don't close mouth for short pauses - for_each_adjacent(shapes.begin(), shapes.end(), [&](const Timed& lhs, const Timed& pause, const Timed& rhs) { + + for_each_adjacent(shapes.begin(), shapes.end(), [&](const Timed& previous, const Timed& pause, const Timed& 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& secondLast, const Timed& last, const Timed& 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;