BookmarkSubscribeRSS Feed
tc
Lapis Lazuli | Level 10 tc
Lapis Lazuli | Level 10

nye2025.gif

 

Here's a Newton's Cradle-inspired animated GIF ball drop to roll out the old year - Happy New Year, all!

 

* Fun With SAS ODS Graphics: New Year's Eve Newton's Cradle Ball Drop
  Info on Newton's Cradle at en.wikipedia.org/wiki/Newton%27s_cradle;

data balls;                                      * Generate x/y points for 4 balls in Newton's Cradle;
r=7.45; y0=8; pi=constant("pi");                 * End balls swing through 30 degree arcs, middle balls stay contant;
do outerframe=0 to 11;                           * 12 time periods - beginning, 10-to-0 seconds countdown, end ending;            
if outerframe=11 then do; mtext='HAPPY NEW YEAR!'; mX=0; mY=8.3; end; * 11th frame displays Happy New Year!; 
ball=1; text='2'; x0=-1.5;                       * Ball one ('2') swings through 30 degree arc;         
frame=0;
do a=90 to 120 by 6, 120 to 96 by -6;
  radians=2*pi*a/360;
  x=x0+r*cos(radians);
  y=y0-r*sin(radians);
  angle=-(a-90);                                 * Ball only swings on alternate time periods;
  if outerframe in (0, 11) or mod(outerframe,2)=0 then do; angle=0; x=x0; y=y0-r; end; 
  frame+1;
  output;
end;                                             * Balls two ('0') and three ('2') are fixed and do not swing;
ball=2; text='0'; x=-.5; x0=-.5; y=y0-r; angle=0; do frame=1 to 11; output; end;
ball=3; text='2'; x=.5; x0=.5; y=y0-r; angle=0; do frame=1 to 11; output; end;
ball=4; text='4'; x0=1.5;
frame=0;
do a=-270 to -300 by -6, -300 to -276 by 6;      * Ball four ('4' or '5') swings through 30 degree arc;
  radians=2*pi*a/360;
  x=x0+r*cos(radians);
  y=y0-r*sin(radians);
  angle=-(a+270);                                * Ball only swings on alternate time periods;
  if outerframe in (0, 11) or ^mod(outerframe,2)=0 then do; angle=0; x=x0; y=y0-r; end; 
  frame+1;
  output;
end;
end;

proc sql;                                        * No arcs for first/last loops, flip year from 2024 --> 2025 on last swing return;
delete from balls where outerframe in (0,11) and frame>1;
update balls set text='5' where ball=4 and (outerframe=11 or (outerframe>=10 and frame>=8));

proc sort data=balls; by outerframe frame;       * Order to allow BY statement to be used in plots;

ods graphics / reset height=8in width=8in noborder;  * Create animated GIF from generated images;
options nobyline papersize=('8 in', '8 in') animduration=2 animloop=yes printerpath=gif animation=start nodate nonumber;
ods printer file='~/nye/nye2025.gif';            * Animated GIF filename;

%macro genplots;                                 /* Use macros to produce 12 sets of frames */
%macro genplot(n);                               /* Each set of frames takes about 1 second (countdown) with pauses before and after */
%if       &n=0  %then options animduration=1.5 %str(; run;); /* Opening frame */ 
%else %if &n=11 %then options animduration=2   %str(; run;); /* Countdown frames */
%else                 options animduration=.1  %str(; run;); /* Final frame (2025) */
                                                 /* Plot the Newton's Cradle (Vector+Ellipse+Text plots) */
proc sgplot data=balls(where=(outerframe=&n)) nowall noautolegend nowall noborder noautolegend;
by outerframe frame;                             /* Use BY statement to create multiple plots for animation */
styleattrs backcolor=black;                      /* Black background */
vector x=x y=y / noarrowheads xorigin=x0 yorigin=y0 lineattrs=(color=cxa9acb6 thickness=2.5pt); /* Strings */
ellipseparm semimajor=.5 semiminor=.5 / xorigin=x yorigin=y nooutline lineattrs=(color=cxa9acb6) fill fillattrs=(color=cxa9acb6); /* Balls */
text x=x y=y text=text / rotate=angle strip textattrs=(color=black size=36pt weight=bold) contributeoffsets=none; /* Year digits */
refline 8.3 / axis=y lineattrs=(thickness=28pt color=cxa9acb6); /* Newton's cradle bar */
text x=mX y=mY text=mText / strip textattrs=(color=black size=20pt weight=bold) contributeoffsets=none; /* Happy New Year! message */
xaxis display=none values=(-5 5) offsetmin=0 offsetmax=0; /* Suppress axis info, limit bounds */
yaxis display=none values=(-1 9) offsetmin=0 offsetmax=0; /* Suppress axis info, limit bounds */
inset %if &n=0 %then "0:10"; %else %if &n=11 %then "0:00"; %else "0:%sysfunc(putn(%eval(10-&n+1),z2.))"; / position=bottom noborder textattrs=(color=cxa9acb6 size=24pt); /* Countdown timer */
run;   
%mend;
%do n=0 %to 11; %genplot(&n); %end;              /* 12 sets of frames */
%mend;
%genplots;
                   
options printerpath=gif animation=stop;          * Stop recording images;
ods printer close;

 

1 REPLY 1
ChrisHemedinger
Community Manager
So clever! Thanks for sharing this.
Register for SAS Innovate 2025!! The premier event for SAS users, May 6-9 in Orlando FL. Sign up now for the best deals!

sas-innovate-wordmark-2025-midnight.png

Register Today!

Join us for SAS Innovate 2025, our biggest and most exciting global event of the year, in Orlando, FL, from May 6-9. Sign up by March 14 for just $795.


Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 1 reply
  • 974 views
  • 12 likes
  • 2 in conversation