@@ -554,113 +554,19 @@ addCanvas(2, (ctx, width, height, animate) => {
554
554
drawClosed ( ctx , interpolateBetween ( percentage , blobA , blobB ) , true ) ;
555
555
} ) ;
556
556
557
- return `Interpolation requires points to be paired up from shape A to B. This means both blobs
558
- must have the same number of points and that the points should be matched in a way that
559
- minimizes movement.` ;
557
+ return `The simplest way to interpolate between blobs would be to move the points that make up
558
+ the blob between the two shapes while running the smoothing pass every frame. The problem
559
+ with this approach is that it doesn't allow for any blob to map to any blob. Specifically it
560
+ would only be possible to animate between blobs that have the same number of points. This
561
+ means something more generic is required.` ;
560
562
} ) ;
561
563
562
564
addCanvas (
563
565
1.3 ,
564
566
( ctx , width , height , animate ) => {
565
- const period = ( Math . E / Math . PI ) * 1000 ;
566
567
const center : Coord = { x : width * 0.5 , y : height * 0.5 } ;
567
-
568
- const blob = centeredBlob (
569
- {
570
- extraPoints : 3 ,
571
- randomness : 6 ,
572
- seed : "shift" ,
573
- size : height * 0.9 ,
574
- } ,
575
- center ,
576
- ) ;
577
-
578
- const shiftedBlob = shift ( 1 , blob ) ;
579
-
580
- let prev = 0 ;
581
- let count = 0 ;
582
- animate ( ( frameTime ) => {
583
- const animationTime = mod ( frameTime , period ) ;
584
- const percentage = timingFunctions . ease ( mod ( animationTime , period ) / period ) ;
585
-
586
- // Count animation loops.
587
- if ( percentage < prev ) count ++ ;
588
- prev = percentage ;
589
-
590
- // Draw lines points are travelling.
591
- tempStyles (
592
- ctx ,
593
- ( ) => {
594
- ctx . fillStyle = colors . secondary ;
595
- ctx . strokeStyle = colors . secondary ;
596
- } ,
597
- ( ) => {
598
- drawPoint ( ctx , center , 2 ) ;
599
- forPoints ( blob , ( { curr, next} ) => {
600
- drawLine ( ctx , curr , next ( ) , 1 , 2 ) ;
601
- } ) ;
602
- } ,
603
- ) ;
604
-
605
- // Pause in-place every other animation loop.
606
- if ( count % 2 === 0 ) {
607
- drawClosed ( ctx , interpolateBetweenSmooth ( 2 , percentage , blob , shiftedBlob ) , true ) ;
608
- } else {
609
- drawClosed ( ctx , blob , true ) ;
610
- }
611
- } ) ;
612
-
613
- return `Points cannot be swapped without resulting in a different shape. However, a likely
614
- enough optimal order can be selected by shifting the points and comparing the point
615
- position deltas.` ;
616
- } ,
617
- ( ctx , width , height , animate ) => {
618
- const period = Math . PI * Math . E * 1000 ;
619
- const center : Coord = { x : width * 0.5 , y : height * 0.5 } ;
620
-
621
- const blob = centeredBlob (
622
- {
623
- extraPoints : 3 ,
624
- randomness : 6 ,
625
- seed : "flip" ,
626
- size : height * 0.9 ,
627
- } ,
628
- center ,
629
- ) ;
630
- const reversedBlob = mapPoints ( blob , ( { curr} ) => {
631
- const temp = curr . handleIn ;
632
- curr . handleIn = curr . handleOut ;
633
- curr . handleOut = temp ;
634
- return curr ;
635
- } ) ;
636
- reversedBlob . reverse ( ) ;
637
-
638
- animate ( ( frameTime ) => {
639
- const percentage = calcBouncePercentage ( period , timingFunctions . ease , frameTime ) ;
640
-
641
- forceStyles ( ctx , ( ) => {
642
- const { pt} = sizes ( ) ;
643
- ctx . fillStyle = "transparent" ;
644
- ctx . lineWidth = pt ;
645
- ctx . strokeStyle = colors . secondary ;
646
- ctx . setLineDash ( [ 2 * pt ] ) ;
647
- drawClosed ( ctx , blob , false ) ;
648
- } ) ;
649
-
650
- drawClosed ( ctx , interpolateBetweenSmooth ( 2 , percentage , blob , reversedBlob ) , true ) ;
651
- } ) ;
652
-
653
- return `The only safe re-ordering is to reverse the points and again iterate through all
654
- possible shifts.` ;
655
- } ,
656
- ) ;
657
-
658
- addCanvas (
659
- 1.3 ,
660
- ( ctx , width , height , animate ) => {
661
- const period = Math . PI * 1000 ;
662
- const center : Coord = { x : width * 0.5 , y : height * 0.5 } ;
663
- const maxExtraPoints = 4 ;
568
+ const maxExtraPoints = 7 ;
569
+ const period = maxExtraPoints * Math . PI * 400 ;
664
570
const { pt} = sizes ( ) ;
665
571
666
572
const blob = centeredBlob (
@@ -695,8 +601,12 @@ addCanvas(
695
601
} ) ;
696
602
} ) ;
697
603
698
- return `Points are added until they both have the same count. These new points should be as
699
- evenly distributed as possible.` ;
604
+ return `The first step to prepare animation is to make the number of points between the
605
+ start and end shapes equal. This is done by adding points to the shape with least points
606
+ until they are both equal.
607
+ <br><br>
608
+ For best animation quality it is important that these points are as evenly distributed
609
+ as possible all around the shape so this is not a recursive algorithm.` ;
700
610
} ,
701
611
( ctx , width , height , animate ) => {
702
612
const period = Math . PI ** Math . E * 1000 ;
@@ -759,11 +669,111 @@ addCanvas(
759
669
) ;
760
670
} ) ;
761
671
762
- return `Curve splitting uses the innermost line from the cubic bezier curve drawing demo and
672
+ return `It is only possible to reliably <i>add</i> points to a blob because attempting to
673
+ remove points without modifying the shape is almost never possible and is expensive to
674
+ compute.
675
+
676
+ Curve splitting uses the innermost line from the cubic bezier curve drawing demo and
763
677
makes either side of the final point the handles.` ;
764
678
} ,
765
679
) ;
766
680
681
+ addCanvas (
682
+ 1.3 ,
683
+ ( ctx , width , height , animate ) => {
684
+ const period = ( Math . E / Math . PI ) * 1000 ;
685
+ const center : Coord = { x : width * 0.5 , y : height * 0.5 } ;
686
+
687
+ const blob = centeredBlob (
688
+ {
689
+ extraPoints : 3 ,
690
+ randomness : 6 ,
691
+ seed : "shift" ,
692
+ size : height * 0.9 ,
693
+ } ,
694
+ center ,
695
+ ) ;
696
+
697
+ const shiftedBlob = shift ( 1 , blob ) ;
698
+
699
+ let prev = 0 ;
700
+ let count = 0 ;
701
+ animate ( ( frameTime ) => {
702
+ const animationTime = mod ( frameTime , period ) ;
703
+ const percentage = timingFunctions . ease ( mod ( animationTime , period ) / period ) ;
704
+
705
+ // Count animation loops.
706
+ if ( percentage < prev ) count ++ ;
707
+ prev = percentage ;
708
+
709
+ // Draw lines points are travelling.
710
+ tempStyles (
711
+ ctx ,
712
+ ( ) => {
713
+ ctx . fillStyle = colors . secondary ;
714
+ ctx . strokeStyle = colors . secondary ;
715
+ } ,
716
+ ( ) => {
717
+ drawPoint ( ctx , center , 2 ) ;
718
+ forPoints ( blob , ( { curr, next} ) => {
719
+ drawLine ( ctx , curr , next ( ) , 1 , 2 ) ;
720
+ } ) ;
721
+ } ,
722
+ ) ;
723
+
724
+ // Pause in-place every other animation loop.
725
+ if ( count % 2 === 0 ) {
726
+ drawClosed ( ctx , interpolateBetweenSmooth ( 2 , percentage , blob , shiftedBlob ) , true ) ;
727
+ } else {
728
+ drawClosed ( ctx , blob , true ) ;
729
+ }
730
+ } ) ;
731
+
732
+ return `Points cannot be swapped without resulting in a different shape. However, a likely
733
+ enough optimal order can be selected by shifting the points and comparing the point
734
+ position deltas.` ;
735
+ } ,
736
+ ( ctx , width , height , animate ) => {
737
+ const period = Math . PI * Math . E * 1000 ;
738
+ const center : Coord = { x : width * 0.5 , y : height * 0.5 } ;
739
+
740
+ const blob = centeredBlob (
741
+ {
742
+ extraPoints : 3 ,
743
+ randomness : 6 ,
744
+ seed : "flip" ,
745
+ size : height * 0.9 ,
746
+ } ,
747
+ center ,
748
+ ) ;
749
+ const reversedBlob = mapPoints ( blob , ( { curr} ) => {
750
+ const temp = curr . handleIn ;
751
+ curr . handleIn = curr . handleOut ;
752
+ curr . handleOut = temp ;
753
+ return curr ;
754
+ } ) ;
755
+ reversedBlob . reverse ( ) ;
756
+
757
+ animate ( ( frameTime ) => {
758
+ const percentage = calcBouncePercentage ( period , timingFunctions . ease , frameTime ) ;
759
+
760
+ forceStyles ( ctx , ( ) => {
761
+ const { pt} = sizes ( ) ;
762
+ ctx . fillStyle = "transparent" ;
763
+ ctx . lineWidth = pt ;
764
+ ctx . strokeStyle = colors . secondary ;
765
+ ctx . setLineDash ( [ 2 * pt ] ) ;
766
+ drawClosed ( ctx , blob , false ) ;
767
+ } ) ;
768
+
769
+ drawClosed ( ctx , interpolateBetweenSmooth ( 2 , percentage , blob , reversedBlob ) , true ) ;
770
+ } ) ;
771
+
772
+ return `The only safe re-ordering is to reverse the points and again iterate through all
773
+ possible shifts.` ;
774
+ } ,
775
+ ) ;
776
+
767
777
addCanvas ( 1.8 , ( ctx , width , height ) => {
768
778
// Only animate in the most recent painter call.
769
779
const animationID = Math . random ( ) ;
0 commit comments