Shape Morphing in Android. Create and draw morph animations on… | by Chet Haase | Android Developers | Apr, 2023

Creating and animating rounded shapes with AndroidX, Part II

Automatic shape morphing — it’s here!

When I first started looking into the problem, it was clear that not only was morphing between shapes a difficult problem, but it’s actually (as far as I can tell) an unsolved research problem.

Animating from a triangle to a rounded rectangle seems very doable
A proposed animation from a triangle to the more complicated shape on the right seems… less obvious

The problem above, for general shapes, is that pesky word: general. Sure, if someone throws an arbitrary complex and discontiguous shape at us, we cannot handle it elegantly. On the other hand, for our purposes, we didn’t need to. That is: we wanted to be able to animate between the curved shapes that this library enables; we didn’t need to solve morphing for all shapes. So we constrained the problem to do just that.

The basic approach to morphing is to take two potentially dissimilar shapes and produce some mapping of one shape to the other, such that the animation is as simple as interpolating between the point values of the mapping. In our case, it means mapping one ordered list of cubic curves (for the starting shape) to another list of cubic curves (for the ending shape). The mapping needs to account for proximity (determining which curves are closest to each other between the shapes) as well as quantity (if the shapes do not have the same number of curves, then new curves will be created to ensure that the mapping is 1:1 — each curve on one shape maps to a specific curve on the other).

Feature Complete

One important thing to note in our approach is our use of features in the shapes, which is our name for the edges and corners of shapes. Specifically, we extract information about where the features occur on each shape, along with the convexity of the corner features. Aligning the structures (detailed in the steps below) uses information about these features so that features morph to similar features whenever possible.

Mid-morph animation from the triangle-ish shape to the star. Note how the features map to each other, minimizing artifacts that might otherwise occur from ignoring features and basing the morph purely on curve proximity.

Morphing Steps

In our implementation, the morph setup process is broken up into three stages:

  • Measurement**: Measurement determines where each curve is located along the overall outline of its shape. This is recorded in an outlineProgress parameter (a value from 0 to 1), indicating where each curve point is between the start (0) and end (1) of the entire shape outline. This step also extracts the features of each shape, recording information about which curves lie along edges, convex corners, or concave corners. This information is important in the later Mapping phase to ensure smooth animations.
  • Mapping: Given the list of features for each shape, we can now create a mapping between those features (e.g., a convex corner on the start shape should map to the nearest convex corner on the end shape). Once that is done, we can similarly map the rest of the curves on the objects between these shapes, based on the relative proximity of these curves to each other between the mapped features.
  • Matching: Once the structures are mapped (for their current features/curves), we can then create the morph structure which matches and maps the curves of both shapes, effectively creating a list of values that can be easily and quickly interpolated between the shapes during the animation.
    An important part of this process is what we call cutting.*** As we walk around both shape outlines, matching the curves on one to the curves on the other, we need to insert curves on potentially both objects to create an overall structure that easily translates from one to the other. For example, in the above example of animating the triangle-ish shape to the star, all four features on the triangle map to similar features on the star, but two additional features for the stars other points need to be inserted in the morph structure so that they can animate into being as we morph from the triangle to the star. This is done by cutting the edge curves on the triangle shape to create the curves that will morph into the convex corners on the star shape.

Once you have two shapes (created with the library APIs described in the previous article), morphing between those shapes requires just two steps:

  1. Create the Morph object
  2. Animate the morph’s progress property
val pointyTriangle = RoundedPolygon(3)
val roundedStar = Star(5, innerRadius = .5f, CornerRounding(.1f))
val morph = Morph(pointyTriangle, roundedStar)
// ... one easy way to run the morph animation...
ObjectAnimator.ofFloat(morph, "progress", 0f, 1f).start()

As I said earlier, the complicated parts of morphing are inside the library (and are drastically simplified by some of the constraints discussed above). The actual use of the API is limited to what you see above: create two shapes, create a morph with them, and animate the morph’s progress.

APIs!

The library is available in alpha form from AndroidX:

Sample code!

The animation at the start of the article was taken from a sample app now hosted on GitHub:

Next Post

How to Get Started with HubSpot Drag and Drop Emails

HubSpot’s drag-and-drop email editor is an easy-to-use tool that lets you drag and drop different elements to quickly build an email that matches your company’s brand guidelines. Before, marketers would have to rely heavily on developers to help create customized emails in HubSpot. Thanks to HubSpot’s drag-and-drop email editor, that’s […]
How to Get Started with HubSpot Drag and Drop Emails