Season's greetings from Phastar's SAS Art competition winners!
Each year Phastar holds an internal competition among its programmers to create a work of festive art, using only their imagination and SAS software!
With so many exciting entries this year, our panel of judges was unable to choose a single winner, and decided to pick two winners instead!
Take a look at our 2022 winners, and scroll down to reveal the SAS code if you'd like to try something similar yourself!
![]() |
![]() |
*CHRISTMAS ART 2022; dm log 'clear; output clear'; *create "page" and set ideal number of snowflakes; %macro centres(); data centres; %let i = 1; %do %while(&i <= &it_max.); *calculate edges of section so that none can overlap, with x1 being the bottom left and x2, x3, x4 going clockwise; %if &i=1 %then %do; data centres; %else %do; *merge with existing sections; proc sql noprint; data centres; %let i = %sysevalf(&i. + 1); %end; %if &it_current >= 700 %then %do; %end; %put &it_current.; %mend; %centres(); ********************************* %macro pentaflake(in=, out=, iterations=); %let n = &iterations.; *let centre dataset be the centre for the pentaflake we want to make; *if n=1 we want to make 5 new points as well as our original; data new_centres; *add your new centres into the points dataset and set the iteration down to go onto the next round; %end; %do n1=2 %to &n.; %let m = %eval(&n1. - 1); %do j=0 %to 5; data new_centres&n1.; %end; data &out.; %mend; %macro p(); *create empty out dataset; %do k=1 %to &it_max.; *subset centres dataset for specific area; *choose number of iterations; %put &num_it.; %pentaflake(in=centres2, out=points&k., iterations=&num_it.); data points; %end; %mend; data final; *add on text; *add on boxes for behind text; title; polygon x=x_box y=y_box id=box_num / fill fillattrs=(color=cx73002e transparency=0.75) outline lineattrs=(color=cxaf95a6 thickness=3 ) ; ods html close; /* proc datasets library=work nolist; %*Register all Windows fonts; dm log 'clear'; %let out_path = P:\Training Area\TrainingCode_UK_US_2020\James P\Graphics; %*Larger snowflakes take more time to render (keep: large < 5, medium < 10, small < 40); %*Total number of snowflakes to render; %*Number of frames in gif; %*Random seed; %*Size of graph; /* %let pi = 3.14159265; /* %global vec2_&name.; %let vec2_&name. = &x. &y.; %mend; /* %global mat2_&name.; %let mat2_&name. = &ii. &ij. &ji. &jj.; %mend; /* %global mat2_&name.; %let cos = %sysfunc(cos(&pi. * &theta. / 180)); %let mat2_&name. = &cos. -&sin. &sin. &cos.; %mend mat2_rot; /* %global vec2_&name.; %let ii = %scan(&&mat2_&mat.., 1, " "); %let ij = %scan(&&mat2_&mat.., 2, " "); %let x = %scan(&&vec2_&vec.., 1, " "); %let vec2_&name. = %sysevalf(&ii. * &x. + &ij. * &y.) %sysevalf(&ji. * &x. + &jj. * &y.); %mend mul; /* %global mat2_&name.; %let m1ii = %scan(&&mat2_&mat1.., 1, " "); %let m1ij = %scan(&&mat2_&mat1.., 2, " "); %let m2ii = %scan(&&mat2_&mat2.., 1, " "); %let m2ij = %scan(&&mat2_&mat2.., 2, " "); %let mat2_&name. = %sysevalf(&m1ii. * &m2ii. + &m1ij. * &m2ji.) %sysevalf(&m1ii. * &m2ij. + &m1ij. * &m2jj.) %mend mmul; /* %let ii = %scan(&&mat2_&mat.., 1, " "); %let ij = %scan(&&mat2_&mat.., 2, " "); data &out. (drop = _&xvar. _&yvar.); id = "&id."; &xvar. = &ii. * _&xvar. + &ij. * _&yvar.; run; /* %let x = %scan(&&vec2_&vec.., 1, " "); data &out. (drop = _&xvar. _&yvar.); id = "&id."; &xvar. = &x. + _&xvar.; run; /* %let i = 1; %mend dvec2; /* %let res_high = 4; /* data temp1; proc sql noprint; data &data. ( keep = id x y); output; run; proc datasets library=work nolist; %mend; /* %vec2(name = center, x = 0, y = 0); %do koch = 1 %to ⁢ %iterate(data = &data., poly = &poly.); %end; %mend koch; /* %vec2(name = center, x = 0, y = 0); %put &=vec2_v1.; %do koch = 1 %to &high.; %iterate(data = polyhigh, poly = polyhigh); %if &koch. = &low. %then %do; %end; %*Generate a low medium and high resolution fractal; %*Halve the size of the low polytope; %*Increase the size of the high polytope; /* %let g = 9.8; %put &=width; %put &=bound_x; /* /* /* %*Temporarily suppress log output (too many datasteps and operations); data polydata; %*Initialize positions, velocities and rotations; do i = 1 to &num.; if i <= &numsmall. then do; pos_x = 2 * &width. * rand("UNIFORM") - &width.; vel_x = ((&wind_max. - &wind_min.) * rand("UNIFORM") + &wind_min.) * cos(constant("pi") * &wind_back. / 180); vel_x = ((&wind_max. - &wind_min.) * rand("UNIFORM") + &wind_min.) * cos(constant("pi") * &wind_fore. / 180); vel_x = 0; scale = 0.25 * rand("UNIFORM") + 0.75; acc_x = 0; rot_x = 360 * rand("UNIFORM"); output; end; %*Put into macro variables; %*Update position vectors and create separate dataset per frame; %do j = 1 %to &num.; /*%put Frame: &=i. of &=frames.; data updated; text2 = "from Phastar"; %*Kinematics; %let pos_x_final = %sysevalf(&exp. * -%sysfunc(cos(&pi. * &i. / 20)) + &&vel_x_&j.. * (&i. - 1) + &&pos_x_&j..); %*Rotations; %*Apply transformations; %mat2_rot(name = rot_z, theta = &rot_z_final.); %mmul(name = rot_final, mat1 = rot_y, mat2 = rot_z); %mmul(name = rot_final, mat1 = rot_final, mat2 = scale); %vec2(name = move, x = &pos_x_final., y = &pos_y_final.); %*Update rotations and scale; %*Update positions; %*Add all polydata; %end; %end; options source notes errors = 1; %mend simulate; %simulate(num = &num., frames = &frames.); /* %*Check ods destination; %*Close ods listing destination; ods graphics / imagefmt=GIF width=5in height=5in; /* each image is 5in x 5in GIF */ /* /* proc sgplot data=polydata nowall noborder; ods pdf close; /*Click here to reveal the SAS code!
proc datasets mt = data lib = work kill nolist; run; quit;
%let xmax = 8.3;
%let ymax = 11.7;
%let it_max = 14;
num=0;
call streaminit(5678);
run;
%let it_current = 1;
*create first centre and radius;
data new;
x_new = rand("Uniform", 0, &xmax.);
y_new = rand("Uniform", 0, &ymax.);
size = rand("Uniform", 0.7, 2.5);
num = 1;
i = &i.;
run;
*reset centres if size puts them over the edge;
data new2;
set new;
if x_new+size>&xmax. then x_new = &xmax. - size;
if x_new-size<0 then x_new = size;
if y_new+size>&ymax. then y_new = &ymax. - size;
if y_new-size<0 then y_new = size;
run;
data new3;
set new2;
*create small r=Rcos(1/5pi);
r = size * cos(0.2*constant("pi"));
*rename size to be bigR, the circumradius of our pentagons;
rename size=bigR ;
run;
%let i = %sysevalf(&i. + 1);
set new3(rename=(x_new=x_centre y_new=y_centre))
centres;
if num ne 0;
run;
%end;
data new4;
merge new3 (keep = x_new y_new bigR num
rename=(bigR = bigR_new))
centres (keep = x_centre y_centre bigR num) ;
by num;
*find the maximum distnace these areas are allowed to be - the two radii from eachother;
d1 = bigR_new + bigR;
*find the length between each centre;
d2 = sqrt( (x_new-x_centre)**2 + (y_new-y_centre)**2 );
*if the distnace between centres is smaller than the two radii then no;
if d2 <= d1 then overlap = "Y";
else overlap = "N";
run;
create table overlap as
select *
from new4
where overlap = "Y";
quit;
%if &sqlobs. = 0 %then %do;
set centres new3(rename=(x_new=x_centre y_new=y_centre));
run;
*find how many posible centres we have used;
%let it_current = %eval(&it_current. + 1);
%let j = %eval(&i.-1);
%put "WARN" "ING: macro has tried 700 possible points without accepting &it_max points. The program will now progress with the &j. accepted points.";
%let i = %eval(&it_max.+1);
%end;
%end;
PENTAFLAKE
*********************************;
data new_centres0;
set &in.;
run;
*if n=0 we want to just record the centre we have and finish;
%if &n. = 0 %then %do;
data &out.;
set new_centres0;
run;
%end;
%if &n >= 1 %then %do;
set new_centres0;
*make r2 smaller than the radius of the whole area;
r2 = r/(1+(2*cos(36*(constant("pi")/180))));
*create d based on radius;
d = 2*r2*cos(36*(constant("pi")/180));
*start at 90 and increase for each c;
direction = (90/&it_max.)*&k.;
*make 5 new points off of the original centre point;
do c1=1 to 5;
*update x and y based on the radius and direction;
cos_num = direction*(constant("pi")/180);
sin_num = direction*(constant("pi")/180);
x_new = x_centre+(d*cos(cos_num));
y_new = y_centre+(d*sin(sin_num));
output;
direction = direction + 72;
end;
drop x_centre y_centre;
run;
*also add new_centres0 so that more points can be made around it;
data new_centres1;
set new_centres0(in=a) new_centres(rename=(y_new=y_centre x_new=x_centre));
if a then do;
c1=0;
r2 = r/(1+(2*cos(36*(constant("pi")/180))));
d = 2*r2*cos(36*(constant("pi")/180));
direction = 90;
end;
n = 1;
run;
data new_centres&n1._&j.;
set new_centres&m.;
where c&m. = &j.;
r2 = r2/(1+(2*cos(36*(constant("pi")/180))));
d = 2*r2*cos(36*(constant("pi")/180));
direction = (90/&it_max.)*&k. + (180*&n1.);
do c&n1.=0 to 5;
*update x and y based on the radius and direction;
cos_num = direction*(constant("pi")/180);
sin_num = direction*(constant("pi")/180);
x_new = x_centre+(d*cos(cos_num));
y_new = y_centre+(d*sin(sin_num));
output;
direction = direction + 72;
end;
drop x_centre y_centre;
run;
%end;
set new_centres&n1._0 - new_centres&n1._5(rename=( y_new=y_centre x_new=x_centre));
n = &n.;
run;
set new_centres0-new_centres&n. ;
n=&n.;
it=&k.;
run;
****************************************
RECURSIVE ALGORITHM FOR PLOTTING CENTRES FOR EACH PENTAGON
****************************************;
data points;
x_centre=.; y_centre=.;
run;
data centres2;
set centres;
where i=&k.;
r2=r;
run;
data num_it;
set centres;
if r < 0.7 then num_it=4;
else num_it=5;
run;
proc sql noprint;
select num_it
into :num_it
from num_it;
run;
set points points&k.;
run;
%p();
set points;
*set the middle point for each snowflake on its own;
if c1=. then do;
x_c1=x_centre; y_c1=y_centre;
end;
*then set the other 5 n=1 centres in a new group;
else if c2=. and c1 ne 0 then do;
x_c2=x_centre; y_c2=y_centre;
end;
*set the rest of the points in their own group;
else if c3=. then do;
x_c3=x_centre; y_c3=y_centre;
end;
else if c4=. then do;
x_c4=x_centre; y_c4=y_centre;
end;
else if c5=. then do;
x_c5=x_centre; y_c5=y_centre;
end;
run;
data text;
length text $20.;
x_tex=&xmax./2; y_tex=6.9; text="Seasons"; output;
x_tex=&xmax./2; y_tex=6.3; text="Greetings"; output;
run;
data box;
length x_box y_box box_num 8.;
input x_box y_box box_num;
datalines;
2.07 5.65 1
2.07 7.45 1
6.23 7.45 1
6.23 5.65 1
1.99 5.57 2
1.99 7.53 2
6.31 7.53 2
6.31 5.57 2
;
data final2(compress=YES);
set final text box;
run;
*****************************************
PLOT
*****************************************;
filename odsout "P:\Training Area\TrainingCode_UK_2021\Maura Brown";
ods graphics / imagename="christmas2022" height=11.7in width=8.3in antialiasmax=200700;
ods _all_ close;
ods html image_dpi=250 path=odsout file="christmas2022.html";
proc sgplot data=final2 noautolegend noborder ;
styleattrs wallcolor=DEGB ;
xaxis display=none type=linear offsetmin=0 offsetmax=0 min=0 max=&xmax.;
yaxis display=none type=linear offsetmin=0 offsetmax=0 min=0 max=&ymax.;
scatter X=x_c5 Y=y_c5 / markerattrs=(symbol=trianglefilled size=2 color=white);
scatter X=x_c4 Y=y_c4 / markerattrs=(symbol=triangledownfilled size=2.5 color=silver) ;
scatter X=x_c3 Y=y_c3 / markerattrs=(symbol=trianglefilled size=3 color=silver);
scatter X=x_c2 Y=y_c2 / markerattrs=(symbol=triangledownfilled size=8 color=VPAB) ;
scatter X=x_c1 Y=y_c1 / markerattrs=(symbol=trianglefilled size=12 color=VPAB );
text x=x_tex y=y_tex text=text / textattrs=(family="Georgia" size=35 color=white ) fillattrs=(transparency=0.5);
run;
* KochSnowflakev3.0
*
* Generates fractals and animates them
* We do not talk about versions 1.0 and 2.0 :(
*/
delete initdata updated poly: mat: vec:;
quit;
proc fontreg mode=add MSGLEVEL=VERBOSE;
fontpath '!SYSTEMROOT\fonts';
run;
%let out_name = snowCard19;
%let out_title = Seasons Greetings;
%let numsmall = 30;
%let nummedium = 6;
%let numlarge = 2;
%let num = %eval(&numsmall. + &nummedium. + &numlarge.);
%let frames = 220;
%let rand_seed = 1340;
%let width = 300;
%let height = 300;
* Set up math utilities
*/
%let err = 2E-6;
* vec2
* creates a two dimensional vector vec2_&name taking in &x and &y as components
* [x]
* [y]
*/
%macro vec2(name = zero, x = 0, y = 0);
* mat2
* creates a two by two matrix mat2_&name taking in &ii, &ij, &ji, &jj as components
* [ii ij]
* [ji jj]
*/
%macro mat2(name = zero, ii = 0, ij = 0, ji = 0, jj = 0);
* mat2_rot
* creates a rotation matrix mat2_rot_&name with a given angle of &theta degrees
*/
%macro mat2_rot(name = zero, theta = 0);
%let sin = %sysfunc(sin(&pi. * &theta. / 180));
* mul
* multiplies a matrix &mat and a vector &vec creating a vector &name
*/
%macro mul(name = , mat = , vec = );
%let ji = %scan(&&mat2_&mat.., 3, " "); %let jj = %scan(&&mat2_&mat.., 4, " ");
%let y = %scan(&&vec2_&vec.., 2, " ");
* mmul
* multiplies the matrix &mat1 by the matrix &mat2 creating a matrix &name
*/
%macro mmul(name = , mat1 = , mat2 = );
%let m1ji = %scan(&&mat2_&mat1.., 3, " "); %let m1jj = %scan(&&mat2_&mat1.., 4, " ");
%let m2ji = %scan(&&mat2_&mat2.., 3, " "); %let m2jj = %scan(&&mat2_&mat2.., 4, " ");
%sysevalf(&m1ji. * &m2ii. + &m1jj. * &m2ji.) %sysevalf(&m1ji. * &m2ij. + &m1jj. * &m2jj.);
* dmul
* multiplies &xvar and &yvar in a dataset &in by the matrix &mat creating a new dataset &out
*/
%macro dmul(mat = , in = , out = , id = , xvar = x, yvar = y);
%let ji = %scan(&&mat2_&mat.., 3, " "); %let jj = %scan(&&mat2_&mat.., 4, " ");
set &in. (rename = (&xvar. = _&xvar. &yvar. = _&yvar.));
&yvar. = &ji. * _&xvar. + &jj. * _&yvar.;
%mend dmul;
* vadd
* adds the vector &vec to &xvar and &yvar in a dataset &in creating a new dataset &out
*/
%macro vadd(vec = , in = , out = , id = , xvar = , yvar = );
%let y = %scan(&&vec2_&vec.., 2, " ");
set &in. (rename = (&xvar. = _&xvar. &yvar. = _&yvar.));
&yvar. = &y. + _&yvar.;
%mend vadd;
* dvec2
* converts a list of vectors &vecs into a dataset &name
*/
%macro dvec2(name = , vecs = );
data &name.;
%do %until(%scan(&vecs., &i., " ") = );
%let vec = %scan(&vecs., &i., " ");
%let x = %scan(&&vec2_&vec.., 1, " ");
%let y = %scan(&&vec2_&vec.., 2, " ");
x = &x.;
y = &y.;
output;
%let i = %eval(&i. + 1);
%end;;
run;
* Generate koch snowflake fractals
*/
%let res_med = 2;
%let res_low = 1;
* iterate
* takes in &data and iterates on the Koch snowflake, applying id &poly
*/
%macro iterate( data = , poly = );
length id $20;
set &data.;
num + 1;
id = "&poly.";
run;
select x, y
into :x_1-, :y_1-
from temp1;
quit;
length id $20;
set temp1;
if num < &sqlobs then do;
x_0 = symget('x_'!!strip(put(num, 8.0)));
y_0 = symget('y_'!!strip(put(num, 8.0)));
x_1 = symget('x_'!!strip(put(num + 1, 8.0)));
y_1 = symget('y_'!!strip(put(num + 1, 8.0)));
x = (2/3) * x_0 + (1/3) * x_1;
y = (2/3) * y_0 + (1/3) * y_1;
id = "&poly.";
output;
x_0 = symget('x_'!!strip(put(num, 8.0)));
y_0 = symget('y_'!!strip(put(num, 8.0)));
x_1 = symget('x_'!!strip(put(num + 1, 8.0)));
y_1 = symget('y_'!!strip(put(num + 1, 8.0)));
x = x_0/2 + x_1/2 +
((y_0 - y_1)*(((x_1 - x_0)**2 + (y_1 - y_0)**2)**0.5)*sin(&pi /3))/(3*(((x_1 - x_0)**2 + (y_0 - y_1)**2)**0.5));
y = y_0/2 + y_1/2 +
((x_1 - x_0)*(((x_1 - x_0)**2 + (y_1 - y_0)**2)**0.5)*sin(&pi /3))/(3*(((x_1 - x_0)**2 + (y_0 - y_1)**2)**0.5));
id = "&poly.";
output;
x_0 = symget('x_'!!strip(put(num, 8.0)));
y_0 = symget('y_'!!strip(put(num, 8.0)));
x_1 = symget('x_'!!strip(put(num + 1, 8.0)));
y_1 = symget('y_'!!strip(put(num + 1, 8.0)));
x = (1/3) * x_0 + (2/3) * x_1;
y = (1/3) * y_0 + (2/3) * y_1;
id = "&poly.";
output;
end;
else do;
x_0 = symget('x_'!!strip(put(num, 8.0)));
y_0 = symget('y_'!!strip(put(num, 8.0)));
x_1 = symget('x_'!!strip(put(1, 8.0)));
y_1 = symget('y_'!!strip(put(1, 8.0)));
x = (2/3) * x_0 + (1/3) * x_1;
y = (2/3) * y_0 + (1/3) * y_1;
id = "&poly.";
output;
x_0 = symget('x_'!!strip(put(num, 8.0)));
y_0 = symget('y_'!!strip(put(num, 8.0)));
x_1 = symget('x_'!!strip(put(1, 8.0)));
y_1 = symget('y_'!!strip(put(1, 8.0)));
x = x_0/2 + x_1/2 +
((y_0 - y_1)*(((x_1 - x_0)**2 + (y_1 - y_0)**2)**0.5)*sin(&pi /3))/(3*(((x_1 - x_0)**2 + (y_0 - y_1)**2)**0.5));
y = y_0/2 + y_1/2 +
((x_1 - x_0)*(((x_1 - x_0)**2 + (y_1 - y_0)**2)**0.5)*sin(&pi /3))/(3*(((x_1 - x_0)**2 + (y_0 - y_1)**2)**0.5));
id = "&poly.";
output;
x_0 = symget('x_'!!strip(put(num, 8.0)));
y_0 = symget('y_'!!strip(put(num, 8.0)));
x_1 = symget('x_'!!strip(put(1, 8.0)));
y_1 = symget('y_'!!strip(put(1, 8.0)));
x = (1/3) * x_0 + (2/3) * x_1;
y = (1/3) * y_0 + (2/3) * y_1;
id = "&poly.";
output;
end;
delete temp1;
quit;
* koch
* Set up initial conditions and iterate to create a koch snowflake of size &size id &poly and
* iterates &it times, adding to the dataset &data.
*/
%macro koch(data = , it = , size = , poly = );
%vec2(name = v1, x = 0, y = %sysevalf(-(&size.)));
%vec2(name = v2, x = %sysevalf(-(&size.) * cos(&pi.*30/180)), y = %sysevalf((&size.) * sin(&pi.*30/180)));
%vec2(name = v3, x = %sysevalf((&size.) * cos(&pi.*30/180)), y = %sysevalf((&size.) * sin(&pi.*30/180)));
%dvec2(name = &data., vecs = v1 v2 v3);
* lmh
* Create different resolutions &low &med and &high of the koch snowflake
*/
%macro lmh(low = , med = , high = );
%vec2(name = v1, x = 0, y = %sysevalf(-(10)));
%vec2(name = v2, x = %sysevalf(-(10) * %sysfunc(cos(&pi.*30/180))), y = %sysevalf((10) * %sysfunc(sin(&pi.*30/180))));
%vec2(name = v3, x = %sysevalf( (10) * %sysfunc(cos(&pi.*30/180))), y = %sysevalf((10) * %sysfunc(sin(&pi.*30/180))));
%dvec2(name = polyhigh, vecs = v1 v2 v3);
%put &=vec2_v2.;
%put &=vec2_v3.;
data polylow;
length id $20;
set polyhigh;
id = "polylow";
run;
%end;
%else %if &koch. = &med. %then %do;
data polymed;
length id $20;
set polyhigh;
id = "polymed";
run;
%end;
%mend lmh;
%lmh(low = &res_low., med = &res_med., high = &res_high.);
%mat2(name = half, ii = 0.5, ij = 0, ji = 0, jj = 0.5);
%dmul(mat = half, in = polylow, out = polylow, id = polylow, xvar = x, yvar = y);
%mat2(name = x10, ii = 10, ij = 0, ji = 0, jj = 10);
%dmul(mat = x10, in = polyhigh, out = polyhigh, id = polyhigh, xvar = x, yvar = y);
* Simulation
*/
%let wind_min = 2;
%let wind_max = 5;
%let wind_fore = 330;
%let wind_back = 280;
%let size_max = 20;
%let bound_x = %sysevalf( &width. + (&size_max. * 0.5));
%let bound_y = %sysevalf( &height. + (&size_max. * 0.5));
%put &=height;
%put &=bound_y;
* simulate
* Simulates movement over &num many snowflakes for &frames many frames
*/
%macro simulate(num = , frames = );
* v = v_0;
* y = v_0*t + p_0;
*/
* a_x = v_w * cos(t)
* a_y = v_w * sin(t) - g
*
* v_x = v_w * sin(t) + v_0_x
* v_y = v_w * -cos(t) - g * t + v_0_y
*
* p_x = v_w * -cos(t) + v_0_x * t + p_0_x
* p_y = v_w * -sin(t) - 0.5 * g * t^2 + v_0_y * t + p_0_y
*/
options nosource nonotes errors=0;
set _null_;
run;
data initdata;
call streaminit(&rand_seed.);
/*pos_x = 8 * &width. * rand("UNIFORM") - 4 * &width.;
pos_y = 2 * &height. * rand("UNIFORM") + 1.5 * &height.;*/
pos_y = 2 * &height. * rand("UNIFORM") - &height.;
vel_y = ((&wind_max. - &wind_min.) * rand("UNIFORM") + &wind_min.) * sin(constant("pi") * &wind_back. / 180);
end;
else if i <= &numsmall. + &nummedium. then do;
pos_x = - &width.;
pos_y = 0.75 * &height.;
vel_y = ((&wind_max. - &wind_min.) * rand("UNIFORM") + &wind_min.) * sin(constant("pi") * &wind_fore. / 180);
end;
else do;
if i = &numsmall. + &nummedium. + 1 then do;
pos_x = &width. + rand("UNIFORM") * 10;
pos_y = &height. + rand("UNIFORM") * 10;
end;
else do;
pos_x = - &width. - rand("UNIFORM") * 10;
pos_y = - &height. - rand("UNIFORM") * 10;
end;
vel_y = 0;
end;
acc_y = 0;
rot_y = 360 * rand("UNIFORM");
rot_z = 360 * rand("UNIFORM");
drop i;
run;
proc sql noprint;
select pos_x, pos_y, vel_x, vel_y, acc_x, acc_y, rot_x, rot_y, rot_z, scale
into :pos_x_1-,
:pos_y_1-,
:vel_x_1-,
:vel_y_1-,
:acc_x_1-,
:acc_y_1-,
:rot_x_1-,
:rot_y_1-,
:rot_z_1-,
:scale_1-
from initdata;
quit;
%do i = 1 %to &frames.;
%put Polygon: &=j. of &=num.;
%put Small: &=numsmall.;
%put Medium: &=nummedium.;*/
%if &j. <= &numsmall. %then %do;
set polylow;
%end;
%else %if &j. <= &numsmall. + &nummedium. %then %do;
set polymed;
%end;
%else %do;
set polyhigh;
%end;
frame = &i.;
%if &i. < 40 %then %do;
text = "Season's Greetings";
%end;
%else %if &i. < 70 %then %do;
text = "from Phastar";
%end;
%else %do;
text = " ";
%end;
tx = 0;
ty = (&i. < 20) * 2 * &height.;
%*tsize = (100 * (&frames. - (&frames. - &i.))) / &frames.; /*lerp*/
tsize = 100;
t2x = 0;
t2y = (&i. < 40) * 2 * &height.;
t2size = 100;
run;
/*
* p_x = v_w * -cos(t) + v_0_x * t + p_0_x
* p_y = v_w * -sin(t) - 0.5 * g * t^2 + v_0_y * t + p_0_y
*/
%if &j. <= &numsmall. %then %do;
%let pos_x_final = %sysevalf(&&vel_x_&j.. * (&i. - 1) + &&pos_x_&j..);
%let pos_y_final = %sysevalf(&&vel_y_&j.. * (&i. - 1) + &&pos_y_&j..);
%if %sysevalf(&pos_x_final. > &bound_x.) %then %do;
%let pos_x_&j. = %sysevalf(&&pos_x_&j.. - 2 * &bound_x.);
%end;
%if %sysevalf(&pos_y_final. < -&bound_y.) %then %do;
%let pos_y_&j. = %sysevalf(&&pos_y_&j.. + 2 * &bound_y.);
%end;
%end;
%else %if &j. <= &numsmall. + &nummedium. %then %do;
%let exp = 100 * %sysfunc(exp(-0.025 * (&i. - 20)**2));
%let pos_y_final = %sysevalf(&exp. * -%sysfunc(sin(&pi. * &i. / 20)) + &&vel_y_&j.. * (&i. - 1) + &&pos_y_&j..);
%end;
%else %do;
%let pos_x_final = %sysevalf(&&pos_x_&j..);
%let pos_y_final = %sysevalf(&&pos_y_&j..);
%end;
%if &j. <= &numsmall. + &nummedium. %then %do;
%let rot_x_final = %sysevalf(&&rot_x_&j. * &i. * 0.01);
%let rot_y_final = %sysevalf(&&rot_y_&j. * &i. * 0.01);
%let rot_z_final = %sysevalf(&&rot_z_&j. * &i. * 0.01);
%end;
%else %do;
%let rot_x_final = 0;
%let rot_y_final = 0;
%let rot_z_final = %sysevalf(&&rot_z_&j. * &i. * 0.0025);
%end;
%mat2(name = scale, ii = &&scale_&j.., jj = &&scale_&j..);
%mat2(name = rot_y, ii = %sysfunc(cos(&pi. * &rot_y_final. / 180)), jj = 1);
%mat2(name = rot_x, ii = 1, jj = %sysfunc(cos(&pi. * &rot_x_final. / 180)));
%mmul(name = rot_final, mat1 = rot_x, mat2 = rot_final);
%dmul(mat = rot_final, in = updated, out = updated, id = polylow_&j._&i., xvar = x, yvar = y);
%vadd(vec = move, in = updated, out = updated, id = polylow_&j._&i., xvar = x, yvar = y);
data polydata;
set polydata
updated;
run;
* Gif output
*/
/*proc print data=sashelp.vdest; run;*/
ods listing close;
options papersize=('5 in', '5 in') /* set size for images */
nodate nonumber /* hide date, time, or frame number */
animduration=0.1 animloop=yes noanimoverlay /* animation details */
printerpath=gif animation=start; /* start recording images to GIF */
ods printer file="&out_path.\&out_name..gif"; /* images saved into animated GIF */
proc sgplot data=polydata nowall noborder noautolegend aspect=1;
%*title "&out_title.";
by frame;
%*inset "Merry Christmas" / TEXTATTRS=(Color=Green Family=Arial Size=8 Style=Italic Weight=Bold)
VALUEALIGN=CENTER;
styleattrs backcolor = cx000000;
polygon X=x Y=y id=id / fill
lineattrs=GraphData3 fillattrs=(color=cxfefeff);
text X=tx Y=ty text=text / POSITION = CENTER
TEXTATTRS = (COLOR='cx73002E' family="Monotype Corsiva")
SIZERESPONSE = tsize
SIZEMAXRESPONSE=100
SIZEMIN=0
SIZEMAX=40;
%*text X=t2x Y=t2y text=text2 / POSITION = CENTER
TEXTATTRS = (COLOR='cx73002E' family="Monotype Corsiva")
SIZERESPONSE = t2size
SIZEMAXRESPONSE=100
SIZEMIN=0
SIZEMAX=40;
xaxis offsetmin=0.02 offsetmax=0.02 min=-&width. max=&width. display=NONE;
yaxis offsetmin=0.02 offsetmax=0.02 min=-&height. max=&height. display=NONE;
run;
options printerpath=gif animation=stop; /* stop recording images */
ods printer close; /* close the animated GIF file */
* Image output
*/
ods graphics / noborder;
ods pdf file="P:\Training Area\TrainingCode_UK_US_2020\James P\Graphics\test.pdf";
styleattrs backcolor = cx000000;
polygon X=x Y=y id=id / fill
lineattrs=GraphData3 fillattrs=(color=cxfefeff);
xaxis min=-100 max=100 display=NONE;
yaxis min=-100 max=100 display=NONE;
run;
ods graphics off;
*/
* Check out the following link for more information on animating with proc sgplot in sas
* https://blogs.sas.com/content/iml/2016/08/22/animation-by-statement-proc-sgplot.html#:~:text=You%20can%20control%20the%20animation%20by%20using%20SAS,PRINTERPATH%3DGIF%20ANIMATION%3DSTOP%20after%20you%20generate%20the%20image%20files.
*/