mirror of
https://github.com/storytold/deckcraft.git
synced 2026-10-24 10:37:03 -06:00
* Morph: a changed geometry morphs its outline (a circle turns into a star) Paired shapes that differ only in geometry (another preset, other adjust values, a custom path) and fill colour no longer cross-fade: one shape moves and its outline turns from the old geometry into the new one. - geom::morph aligns two outlines the way manim does: the same number of contours, the same number of cubic curves per contour pair, then every point moves in a straight line. Closed contours are turned and reversed so the points travel least (a triangle does not twist into a circle), extra points go to the longest curves, and a sub-path without partner grows out of (or shrinks into) a point, so a cube's faces appear and a donut's hole closes. Fill mode, outline and fill rule switch at the midpoint. - anim::morph_frame lists such shapes in MorphFrame::paths; the renderer draws them with the morphed geometry (PathMorph, a new render_blend argument next to TextMorph). Ported from the path morph in alexplain (MIT, same author). generated with Claude Code * Morph: differing looks of an outline cross-fade instead of switching When a sub-path pair differs in fill mode, outline or fill rule, the two looks no longer switch at the midpoint. A fill or outline only one side has fades out (or in), so a filled shape turning into a line loses its fill gradually. When both fill, the even-odd side lies opaque underneath and the other fades over it, so a donut's hole fills smoothly while it shrinks. generated with Claude Code --------- Co-authored-by: Brandon Thomas <echelon@gmail.com>
41 lines
1.6 KiB
Rust
41 lines
1.6 KiB
Rust
//! Morph of an outline: a frame shape whose geometry turns from the old shape's into its own (a
|
|
//! circle into a star), with the points aligned by `deckcraft_geom::morph`.
|
|
|
|
use deckcraft_geom::Xfrm;
|
|
use deckcraft_geom::morph::{Fade, GeometryMorph};
|
|
use deckcraft_geom::preset::Geometry;
|
|
use deckcraft_model::{Geom, Shape, ShapeId};
|
|
use kurbo::Vec2;
|
|
|
|
use crate::shape_geometry;
|
|
|
|
/// A frame shape (see [`crate::render_blend`]) whose outline morphs from `from` into its own
|
|
/// geometry.
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct PathMorph {
|
|
pub id: ShapeId,
|
|
/// The old shape's geometry.
|
|
pub from: Geom,
|
|
/// The boxes of the old and the new shape on their own slides.
|
|
pub from_box: Xfrm,
|
|
pub to_box: Xfrm,
|
|
/// Progress, 0..1 (already eased).
|
|
pub t: f64,
|
|
}
|
|
|
|
/// `now / own`, or 1 when that is no usable scale (a line's zero height).
|
|
fn ratio(now: f64, own: f64) -> f64 {
|
|
let r = now / own;
|
|
if own.abs() > 1e-9 && r.is_finite() { r } else { 1.0 }
|
|
}
|
|
|
|
/// The geometry of frame shape `s` at its current size `w` x `h`: both outlines are built at their
|
|
/// own sizes (so the alignment does not change while the box grows), then scaled into the box.
|
|
/// With a [`Fade`] per sub-path.
|
|
pub(crate) fn geometry(pm: &PathMorph, s: &Shape, w: f64, h: f64) -> (Geometry, Vec<Fade>) {
|
|
let a = shape_geometry(&Shape { geom: pm.from.clone(), ..Shape::default() }, pm.from_box.w, pm.from_box.h);
|
|
let b = shape_geometry(s, pm.to_box.w, pm.to_box.h);
|
|
let scale = |x: Xfrm| Vec2::new(ratio(w, x.w), ratio(h, x.h));
|
|
GeometryMorph::new(&a, &b).at(pm.t, scale(pm.from_box), scale(pm.to_box))
|
|
}
|