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.
SAS For Dummies 3rd Edition! Check out the new edition, covering SAS 9.4, SAS Viya, and all of the modern ways to use SAS!

hackathon24-white-horiz.png

The 2025 SAS Hackathon Kicks Off on June 11!

Watch the live Hackathon Kickoff to get all the essential information about the SAS Hackathon—including how to join, how to participate, and expert tips for success.

YouTube LinkedIn

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
  • 1549 views
  • 12 likes
  • 2 in conversation