From 7f166b71592a8d1936ec8572e3221e9ffe3297a5 Mon Sep 17 00:00:00 2001 From: Gabriel Harel Date: Wed, 13 Sep 2023 22:45:42 -0500 Subject: [PATCH] update content --- demo/content.ts | 218 +++++++++++++++++++++++++----------------------- 1 file changed, 114 insertions(+), 104 deletions(-) diff --git a/demo/content.ts b/demo/content.ts index 50fd035..434951f 100644 --- a/demo/content.ts +++ b/demo/content.ts @@ -554,113 +554,19 @@ addCanvas(2, (ctx, width, height, animate) => { drawClosed(ctx, interpolateBetween(percentage, blobA, blobB), true); }); - return `Interpolation requires points to be paired up from shape A to B. This means both blobs - must have the same number of points and that the points should be matched in a way that - minimizes movement.`; + return `The simplest way to interpolate between blobs would be to move the points that make up + the blob between the two shapes while running the smoothing pass every frame. The problem + with this approach is that it doesn't allow for any blob to map to any blob. Specifically it + would only be possible to animate between blobs that have the same number of points. This + means something more generic is required.`; }); addCanvas( 1.3, (ctx, width, height, animate) => { - const period = (Math.E / Math.PI) * 1000; const center: Coord = {x: width * 0.5, y: height * 0.5}; - - const blob = centeredBlob( - { - extraPoints: 3, - randomness: 6, - seed: "shift", - size: height * 0.9, - }, - center, - ); - - const shiftedBlob = shift(1, blob); - - let prev = 0; - let count = 0; - animate((frameTime) => { - const animationTime = mod(frameTime, period); - const percentage = timingFunctions.ease(mod(animationTime, period) / period); - - // Count animation loops. - if (percentage < prev) count++; - prev = percentage; - - // Draw lines points are travelling. - tempStyles( - ctx, - () => { - ctx.fillStyle = colors.secondary; - ctx.strokeStyle = colors.secondary; - }, - () => { - drawPoint(ctx, center, 2); - forPoints(blob, ({curr, next}) => { - drawLine(ctx, curr, next(), 1, 2); - }); - }, - ); - - // Pause in-place every other animation loop. - if (count % 2 === 0) { - drawClosed(ctx, interpolateBetweenSmooth(2, percentage, blob, shiftedBlob), true); - } else { - drawClosed(ctx, blob, true); - } - }); - - return `Points cannot be swapped without resulting in a different shape. However, a likely - enough optimal order can be selected by shifting the points and comparing the point - position deltas.`; - }, - (ctx, width, height, animate) => { - const period = Math.PI * Math.E * 1000; - const center: Coord = {x: width * 0.5, y: height * 0.5}; - - const blob = centeredBlob( - { - extraPoints: 3, - randomness: 6, - seed: "flip", - size: height * 0.9, - }, - center, - ); - const reversedBlob = mapPoints(blob, ({curr}) => { - const temp = curr.handleIn; - curr.handleIn = curr.handleOut; - curr.handleOut = temp; - return curr; - }); - reversedBlob.reverse(); - - animate((frameTime) => { - const percentage = calcBouncePercentage(period, timingFunctions.ease, frameTime); - - forceStyles(ctx, () => { - const {pt} = sizes(); - ctx.fillStyle = "transparent"; - ctx.lineWidth = pt; - ctx.strokeStyle = colors.secondary; - ctx.setLineDash([2 * pt]); - drawClosed(ctx, blob, false); - }); - - drawClosed(ctx, interpolateBetweenSmooth(2, percentage, blob, reversedBlob), true); - }); - - return `The only safe re-ordering is to reverse the points and again iterate through all - possible shifts.`; - }, -); - -addCanvas( - 1.3, - (ctx, width, height, animate) => { - const period = Math.PI * 1000; - const center: Coord = {x: width * 0.5, y: height * 0.5}; - const maxExtraPoints = 4; + const maxExtraPoints = 7; + const period = maxExtraPoints * Math.PI * 400; const {pt} = sizes(); const blob = centeredBlob( @@ -695,8 +601,12 @@ addCanvas( }); }); - return `Points are added until they both have the same count. These new points should be as - evenly distributed as possible.`; + return `The first step to prepare animation is to make the number of points between the + start and end shapes equal. This is done by adding points to the shape with least points + until they are both equal. +

+ For best animation quality it is important that these points are as evenly distributed + as possible all around the shape so this is not a recursive algorithm.`; }, (ctx, width, height, animate) => { const period = Math.PI ** Math.E * 1000; @@ -759,11 +669,111 @@ addCanvas( ); }); - return `Curve splitting uses the innermost line from the cubic bezier curve drawing demo and + return `It is only possible to reliably add points to a blob because attempting to + remove points without modifying the shape is almost never possible and is expensive to + compute. + + Curve splitting uses the innermost line from the cubic bezier curve drawing demo and makes either side of the final point the handles.`; }, ); +addCanvas( + 1.3, + (ctx, width, height, animate) => { + const period = (Math.E / Math.PI) * 1000; + const center: Coord = {x: width * 0.5, y: height * 0.5}; + + const blob = centeredBlob( + { + extraPoints: 3, + randomness: 6, + seed: "shift", + size: height * 0.9, + }, + center, + ); + + const shiftedBlob = shift(1, blob); + + let prev = 0; + let count = 0; + animate((frameTime) => { + const animationTime = mod(frameTime, period); + const percentage = timingFunctions.ease(mod(animationTime, period) / period); + + // Count animation loops. + if (percentage < prev) count++; + prev = percentage; + + // Draw lines points are travelling. + tempStyles( + ctx, + () => { + ctx.fillStyle = colors.secondary; + ctx.strokeStyle = colors.secondary; + }, + () => { + drawPoint(ctx, center, 2); + forPoints(blob, ({curr, next}) => { + drawLine(ctx, curr, next(), 1, 2); + }); + }, + ); + + // Pause in-place every other animation loop. + if (count % 2 === 0) { + drawClosed(ctx, interpolateBetweenSmooth(2, percentage, blob, shiftedBlob), true); + } else { + drawClosed(ctx, blob, true); + } + }); + + return `Points cannot be swapped without resulting in a different shape. However, a likely + enough optimal order can be selected by shifting the points and comparing the point + position deltas.`; + }, + (ctx, width, height, animate) => { + const period = Math.PI * Math.E * 1000; + const center: Coord = {x: width * 0.5, y: height * 0.5}; + + const blob = centeredBlob( + { + extraPoints: 3, + randomness: 6, + seed: "flip", + size: height * 0.9, + }, + center, + ); + const reversedBlob = mapPoints(blob, ({curr}) => { + const temp = curr.handleIn; + curr.handleIn = curr.handleOut; + curr.handleOut = temp; + return curr; + }); + reversedBlob.reverse(); + + animate((frameTime) => { + const percentage = calcBouncePercentage(period, timingFunctions.ease, frameTime); + + forceStyles(ctx, () => { + const {pt} = sizes(); + ctx.fillStyle = "transparent"; + ctx.lineWidth = pt; + ctx.strokeStyle = colors.secondary; + ctx.setLineDash([2 * pt]); + drawClosed(ctx, blob, false); + }); + + drawClosed(ctx, interpolateBetweenSmooth(2, percentage, blob, reversedBlob), true); + }); + + return `The only safe re-ordering is to reverse the points and again iterate through all + possible shifts.`; + }, +); + addCanvas(1.8, (ctx, width, height) => { // Only animate in the most recent painter call. const animationID = Math.random();