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-white.png

🚨 Early Bird Rate Extended!

Join us for SAS Innovate 2025, our biggest and most exciting global event of the year, in Orlando, FL, from May 6-9.

 

Lock in the best rate now before the price increases on April 1.

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