Animations - #127
Conversation
| let (transOriginAttr, transElement) = case getAttr sty of | ||
| Nothing -> ([], mempty) | ||
| Just (TransformAnimationAttribute animations t) -> | ||
| ( pure . makeAttribute "transform-origin" . (\(P (V2 x y)) -> showNum x <> " " <> showNum y) $ transform t 1 |
There was a problem hiding this comment.
This looks very slightly off for my working example (by about V2 -3.3 3.3 for small circles, tending to 0 for larger ones). Not sure why. I guess I should come up with a simpler example anyway, to post in this thread as motivation.
There was a problem hiding this comment.
I think I fixed this by changing 1 to 0, which makes sense as we should only care about translations to the origin.
| , flip foldMap animations $ \(TransformAnimation dur rep animation) -> animateTransform_ $ | ||
| [ AttributeName_ <<- "transform" | ||
| , Additive_ <<- "sum" | ||
| , Dur_ <<- T.show dur <> "s" |
There was a problem hiding this comment.
Ah, T.show is causing CI to fail for all GHCs before 9.12. Guess I'll have to do this the old-fashioned way.
| gDefs `mappend` g_ (R.renderStyles idFill idLine sty) clippedSvg | ||
| gDefs `mappend` g_ (transOriginAttr <> R.renderStyles idFill idLine sty) (transElement <> clippedSvg) | ||
| where | ||
| showNum = T.pack . showFixed @E3 True . realToFrac |
There was a problem hiding this comment.
I've gone with three decimal places based on looking at existing numbers in the SVG output. Presumably there's some code for those which I should be reusing here?
| TransformAnimationType | ||
| data TransformAnimationType | ||
| = ScaleAnimation [V2 Double] | ||
| | TranslateAnimation [V2 Double] |
There was a problem hiding this comment.
We should use stronger types here, possibly records, better names, add support for rotations etc...
But actually, perhaps we should just generalise and take actual transformations, i.e. a [T2 Double]? Can all transformations be decomposed in to a combination of scaling, translating and rotating and maybe skewing? As these are the operations that <animateTransform> accepts.
There was a problem hiding this comment.
Yes, all affine transformations can be decomposed into a combination of translation + rotation + scaling + shear (and shear can itself actually be decomposed into rotation + scaling), though there is not necessarily a unique decomposition. For example, see https://research.cs.wisc.edu/graphics/Courses/838-s2002/Papers/polar-decomp.pdf .
I don't understand this code well enough yet to have an opinion on whether generalizing to take actual transformations would be a good idea.
| type instance N TransformAnimationAttribute = Double | ||
| instance Semigroup TransformAnimationAttribute where | ||
| TransformAnimationAttribute xs tx <> TransformAnimationAttribute ys ty = | ||
| TransformAnimationAttribute (xs <> ys) (tx <> ty) |
There was a problem hiding this comment.
Not sure this is correct... But maybe if we moved to taking actual transformations as mentioned above, this would sort of solve itself.
There was a problem hiding this comment.
Why wouldn't this be correct? The mapping from [TransformAnimation] to Transformation V2 Double should be a monoid homomorphism, so this should work just fine (unless the order is wrong? I don't understand it well enough yet to say whether it should be tx <> ty or ty <> tx, but I do know that it bears careful thinking about).
There was a problem hiding this comment.
Why wouldn't this be correct?
Simply because I haven't thought about it deeply, or heavily tested it yet. I just wrote the obvious thing.
There was a problem hiding this comment.
Oh, OK, given #127 (comment) , then I don't think this is correct. Transformations do not commute in general, and simply composing the two different transformations separately does not seem to respect or consider the order in which they should be applied. Perhaps some kind of semidirect product is needed, but I am not sure at the moment.
Co-authored-by: Patrick Aldis <patrickaldis@users.noreply.github.com>
| TransformAnimationAttribute (xs <> ys) (tx <> ty) | ||
| instance Transformable TransformAnimationAttribute where | ||
| transform t (TransformAnimationAttribute a t0) = | ||
| TransformAnimationAttribute a (t <> t0) |
There was a problem hiding this comment.
This, on the other hand, does not seem right at all. Applying a transformation to the stored Transform V2 Double but doing nothing to the a breaks the invariant that the stored Transform corresponds to the list of TransformAnimations, and after that point all bets are off.
There was a problem hiding this comment.
Wait a minute, maybe that is not the invariant after all...
There was a problem hiding this comment.
Wait a minute, maybe that is not the invariant after all...
Yes, that's not the intention. See #127 (comment).
|
|
||
| data TransformAnimationAttribute = TransformAnimationAttribute | ||
| [TransformAnimation] | ||
| (Transformation V2 Double) |
There was a problem hiding this comment.
What do the fields of the TransformAnimationAttribute data type represent?
There was a problem hiding this comment.
The first field is the transformations we want to apply, TransformAnimation corresponding to an <animateTransform>.
The second field keeps track of the transformations which have been applied to the animated element. You can see that we use it in attributedRender for setting the transform origin and for rescaling translations. There might be a more direct way to get hold of this? I'm not really that familiar with Diagrams' internals.
@georgefst it's not secret but you have to know where to look: the The basic semantics of |
I know that feeling!
I would love to get in to it, but I don't know if I'll have the time. It's unlikely I can carve it out during work hours, since this already does what we need for now for prototyping, and we'll probably move to a different approach once we need interactivity. I know it's a long way off, but are you likely to be at Zurihac again in June? I'd hoped to say hi this year after your talk, but I was unfortunately stuck in my hotel for most of the weekend due to illness. |
Unless |
Fair enough!
It's unlikely, unless someone wants to pay for me to come (last year ZuriHac paid for my travel since I was an invited speaker). Being at a small institution I don't have much funding for international travel.
It very well could be! That is, I certainly aspire/intend for it to be. But I don't know how much work would still be required to get everything to work. |
b5bcb04 to
980f61e
Compare
This is obviously a work-in-progress, but I wanted to get feedback as early as possible, in particular on whether something like this is likely to be accepted upstream.
This uses the
animateTransformelement to animate arbitrary subdiagrams. It's part of the standard, though unfortunately not yet widely supported by non-web SVG viewers (I've been using Gnome's Epiphany web browser for testing, as it's the only tool I've found which shows animations and auto-reloads on changes to a local file). We should also supportanimateMotion, potentially taking a DiagramsPath, as this allows for translations along a smooth curve. There's alsoanimate, but that seems too ad-hoc to be useful in the context of Diagrams.I've linked to MDN there, but actually much of this API is poorly-documented everywhere but the official spec. In particular, I've seen no good explanation elsewhere of the
valuesattribute, which we use here as it's necessary for any animations which go through multiple states, rather than just smoothly from A to B. We should check this spec to make sure we cover everything important in our API surface.@byorgey I know you've hinted at developing a completely new approach to animations in Diagrams for a long time. I'd love to know more about what sort of API you're aiming for, as I've been unable to find any details. I have always assumed it's still fundamentally similar to the general-but-inefficient "lists of diagrams" stuff that one sees in e.g.
diagrams-rasterificfor GIF generation. But perhaps you had an abstraction in mind which would support native animations in the SVG backend?Regardless, this SVG-specific approach has been highly useful, especially with Reanimate seemingly no longer maintained, and it's allowed us to avoid needing #126.
CC @patrickaldis @cgibbard