SAS Super FREQ

## Patterns of polygons: Use SAS to create mathematical art

An artist named Odili Donald Odita painted a beautiful picture ("Phantom’s Shadow, 2018") that contains geometric polygons in different colors and orientations. A reproduction of Odita's image is shown below:

In a blog post, I show how to use symmetry and PROC SGPANEL to create tilings that are inspired by Odita's work. I analyze some of the mathematical structures in Odita's painting and creates the following image in SAS; see the blog post for its mathematical significance:

You can create images like these from basic mathematical principles. To simplify the process, the following data set contains the 16 pinwheel-shaped images that appear in the tiling above. The SAS program creates the mathematical version of Odita's artwork.

``````data Pinwheels;
input Group ID @;
do i = 1 to 5;
input x y @@;
output;
end;
drop i;
datalines;
0 0 0 0   0.667  0.333   1  1   0  1  0 0
0 1 0 0  -0.333  0.667  -1  1  -1  0  0 0
0 2 0 0  -0.667 -0.333  -1 -1   0 -1  0 0
0 3 0 0   0.333 -0.667   1 -1   1  0  0 0
1 0 0 0  -0.333  0.667  -1  1  -1  0  0 0
1 1 0 0  -0.667 -0.333  -1 -1   0 -1  0 0
1 2 0 0   0.333 -0.667   1 -1   1  0  0 0
1 3 0 0   0.667  0.333   1  1   0  1  0 0
2 0 0 0  -0.667 -0.333  -1 -1   0 -1  0 0
2 1 0 0   0.333 -0.667   1 -1   1  0  0 0
2 2 0 0   0.667  0.333   1  1   0  1  0 0
2 3 0 0  -0.333  0.667  -1  1  -1  0  0 0
3 0 0 0   0.333 -0.667   1 -1   1  0  0 0
3 1 0 0   0.667  0.333   1  1   0  1  0 0
3 2 0 0  -0.333  0.667  -1  1  -1  0  0 0
3 3 0 0  -0.667 -0.333  -1 -1   0 -1  0 0
4 0 0 0  -0.667  0.333  -1  1   0  1  0 0
4 1 0 0   0.333  0.667   1  1   1  0  0 0
4 2 0 0   0.667 -0.333   1 -1   0 -1  0 0
4 3 0 0  -0.333 -0.667  -1 -1  -1  0  0 0
5 0 0 0   0.333  0.667   1  1   1  0  0 0
5 1 0 0   0.667 -0.333   1 -1   0 -1  0 0
5 2 0 0  -0.333 -0.667  -1 -1  -1  0  0 0
5 3 0 0  -0.667  0.333  -1  1   0  1  0 0
6 0 0 0  -0.333 -0.667  -1 -1  -1  0  0 0
6 1 0 0  -0.667  0.333  -1  1   0  1  0 0
6 2 0 0   0.333  0.667   1  1   1  0  0 0
6 3 0 0   0.667 -0.333   1 -1   0 -1  0 0
7 0 0 0   0.667 -0.333   1 -1   0 -1  0 0
7 1 0 0  -0.333 -0.667  -1 -1  -1  0  0 0
7 2 0 0  -0.667  0.333  -1  1   0  1  0 0
7 3 0 0   0.333  0.667   1  1   1  0  0 0
8 0 0 0   0.667  0.333   1  1   0  1  0 0
8 3 0 0  -0.333  0.667  -1  1  -1  0  0 0
8 2 0 0  -0.667 -0.333  -1 -1   0 -1  0 0
8 1 0 0   0.333 -0.667   1 -1   1  0  0 0
9 0 0 0  -0.333  0.667  -1  1  -1  0  0 0
9 3 0 0  -0.667 -0.333  -1 -1   0 -1  0 0
9 2 0 0   0.333 -0.667   1 -1   1  0  0 0
9 1 0 0   0.667  0.333   1  1   0  1  0 0
10 0 0 0  -0.667 -0.333  -1 -1  0 -1  0 0
10 3 0 0   0.333 -0.667   1 -1   1  0  0 0
10 2 0 0   0.667  0.333   1  1   0  1  0 0
10 1 0 0  -0.333  0.667  -1  1  -1  0  0 0
11 0 0 0   0.333 -0.667   1 -1   1  0  0 0
11 3 0 0   0.667  0.333   1  1   0  1  0 0
11 2 0 0  -0.333  0.667  -1  1  -1  0  0 0
11 1 0 0  -0.667 -0.333  -1 -1   0 -1  0 0
12 0 0 0  -0.667  0.333  -1  1   0  1  0 0
12 3 0 0   0.333  0.667   1  1   1  0  0 0
12 2 0 0   0.667 -0.333   1 -1   0 -1  0 0
12 1 0 0  -0.333 -0.667  -1 -1  -1  0  0 0
13 0 0 0   0.333  0.667   1  1   1  0  0 0
13 3 0 0   0.667 -0.333   1 -1   0 -1  0 0
13 2 0 0  -0.333 -0.667  -1 -1  -1  0  0 0
13 1 0 0  -0.667  0.333  -1  1   0  1  0 0
14 0 0 0  -0.333 -0.667  -1 -1  -1  0  0 0
14 3 0 0  -0.667  0.333  -1  1   0  1  0 0
14 2 0 0   0.333  0.667   1  1   1  0  0 0
14 1 0 0   0.667 -0.333   1 -1   0 -1  0 0
15 0 0 0   0.667 -0.333   1 -1   0 -1  0 0
15 3 0 0  -0.333 -0.667  -1 -1  -1  0  0 0
15 2 0 0  -0.667  0.333  -1  1   0  1  0 0
15 1 0 0   0.333  0.667   1  1   1  0  0 0
;

%let teal   = CX288c95;
%let orange = CXeba411;
%let blue   = CX0f5098;
%let salmon = CXd5856e;
%let gray   = CX929386;

ods graphics / width=640px height=640px;
%macro colOpts; colaxis offsetmin=0 offsetmax=0 display=(nolabel noticks novalues); %mend;
%macro rowOpts;  rowaxis offsetmin=0 offsetmax=0 display=(nolabel noticks novalues); %mend;

proc sgpanel data=Pinwheels noautolegend;
styleattrs wallcolor=&gray datacolors=(&teal &orange &blue &salmon);
panelby Group / columns=4 onepanel noheader;
polygon x=x y=y ID=ID / group=ID fill;
%colOpts; %rowOpts;
run;``````

Here is a challenge for SAS programmers: create similar images! Who is up for the challenge? Use these 16 polygons in whatever order you want to create an Odita-inspired image, or create your own polygons.

Post your SAS code and a small image that shows your creativity and programming skills!

13 REPLIES 13
Super User

## Re: Patterns of polygons: Use SAS to create mathematical art

Of course. Calling out @tc  @GraphGuy

Super User

## Re: Patterns of polygons: Use SAS to create mathematical art

Rick,

OK. I post my code here .

``````%let alpha = 0.333;          /* Odita's work uses a vertex as (1-alpha, alpha) */
data Poly1;
ID = 1;
input x y @@;
if x=. then do;              /* just for fun, support other locations of vertex */
x = 1 - &alpha; y = &alpha;
end;
datalines;
0 0   . .   1 1   0 1   0 0
;

%macro rotation(dsn=);
%do i=0 %to 7;
%do j=0 %to 7;
%let id=%sysevalf(%sysfunc(rand(uniform))*99999,int);
%let type=%sysfunc(rand(table,0.25,0.25,0.25));

%if &type=1 %then %let angle=0;
%else %if &type=2 %then %let angle=90;
%else %if &type=3 %then %let angle=180;
%else %if  &type=4 %then %let angle=270;

data temp(keep=id group x_rotated y_rotated rename=(x_rotated=x y_rotated=y));
set &dsn. ;
id=&id.;
group=&type.;

%if &type=2 %then %do;
x_rotated=x_rotated+1;
%end;
%else %if &type=3 %then %do;
x_rotated=x_rotated+1;
y_rotated=y_rotated+1;
%end;
%else %if &type=4 %then %do;
y_rotated=y_rotated+1;
%end;

x_rotated=x_rotated+&i.;
y_rotated=y_rotated+&j.;
run;
proc append base=want data=temp force;run;
%end;
%end;
%mend;

proc delete data=want;run;
%rotation(dsn=Poly1)

%let blue   = CX0f5098;
%let orange = CXeba411;
%let teal   = CX288c95;
%let salmon = CXd5856e;
%let gray   = CX929386;

ods graphics / width=480px height=480px noborder;
title "Base Polygon";
proc sgplot data=want aspect=1 noborder noautolegend;
styleattrs wallcolor=&gray datacolors=(&teal &orange &blue &salmon);
polygon x=x y=y ID=ID / group=group fill;
xaxis display=none offsetmax=0 offsetmin=0 ;
yaxis display=none offsetmax=0 offsetmin=0 ;
run;``````

Super User

## Re: Patterns of polygons: Use SAS to create mathematical art

Rick,

Here is another one . the same with  Odili's .

``````%let alpha = 0.3;          /* Odita's work uses a vertex as (1-alpha, alpha) */
data Poly1;
ID = 1;
input x y @@;
if x=. then do;              /* just for fun, support other locations of vertex */
x = 1 - &alpha; y = &alpha;
end;
datalines;
0 0   . .   1 1   0 1   0 0
;
data Poly2;
ID = 1;
input x y @@;
if x=. then do;              /* just for fun, support other locations of vertex */
x = &alpha; y = 1 -  &alpha;
end;
datalines;
0 0   1 0   1 1   . .   0 0
;

proc format;
invalue color_a
0  =1
90 =2
180=3
270=4;
invalue color_b
0  =3
90 =2
180=1
270=4;
invalue color_c
0  =4
90 =1
180=2
270=3;
invalue color_d
0  =2
90 =1
180=4
270=3;
invalue color_e
0  =3
90 =4
180=1
270=2;
invalue color_f
0  =1
90 =4
180=3
270=2;
invalue color_g
0  =2
90 =3
180=4
270=1;
invalue color_h
0  =4
90 =3
180=2
270=1;
run;

%macro rotation(dsn= ,color=,x=,y=);
%let angles=0 90 180 270;
%let group=%sysfunc(rand(integer,1,999999));
%do i=1 %to 4;
%let angle=%scan(&angles.,&i.,%str( ));
data temp(keep=id group x_rotated y_rotated rename=(x_rotated=x y_rotated=y));
set &dsn. ;
%if &color.=1 %then %do;id=inputn(&angle.,"color_a.");%end;
%if &color.=2 %then %do;id=inputn(&angle.,"color_b.");%end;
%if &color.=3 %then %do;id=inputn(&angle.,"color_c.");%end;
%if &color.=4 %then %do;id=inputn(&angle.,"color_d.");%end;
%if &color.=5 %then %do;id=inputn(&angle.,"color_e.");%end;
%if &color.=6 %then %do;id=inputn(&angle.,"color_f.");%end;
%if &color.=7 %then %do;id=inputn(&angle.,"color_g.");%end;
%if &color.=8 %then %do;id=inputn(&angle.,"color_h.");%end;
x_rotated=x_rotated+&x.;
y_rotated=y_rotated+&y.;
group=&group.;
run;
proc append base=want data=temp force;run;
%end;
%mend;

proc delete data=want;run;
%rotation(dsn=Poly1,color=1,x=6,y=6)
%rotation(dsn=Poly2,color=2,x=4,y=6)
%rotation(dsn=Poly2,color=3,x=2,y=6)
%rotation(dsn=Poly1,color=4,x=0,y=6)

%rotation(dsn=Poly2,color=4,x=6,y=4)
%rotation(dsn=Poly1,color=5,x=4,y=4)
%rotation(dsn=Poly1,color=6,x=2,y=4)
%rotation(dsn=Poly2,color=7,x=0,y=4)

%rotation(dsn=Poly2,color=7,x=6,y=2)
%rotation(dsn=Poly1,color=8,x=4,y=2)
%rotation(dsn=Poly1,color=1,x=2,y=2)
%rotation(dsn=Poly2,color=2,x=0,y=2)

%rotation(dsn=Poly1,color=2,x=6,y=0)
%rotation(dsn=Poly2,color=3,x=4,y=0)
%rotation(dsn=Poly2,color=4,x=2,y=0)
%rotation(dsn=Poly1,color=5,x=0,y=0)

%let blue   = CX0f5098;
%let orange = CXeba411;
%let teal   = CX288c95;
%let salmon = CXd5856e;
%let gray   = CX929386;

ods graphics / width=480px height=480px noborder;
title "Base Polygon";
proc sgplot data=want aspect=1 noborder noautolegend;
styleattrs wallcolor=&gray datacolors=(&teal &orange &blue &salmon);
polygon x=x y=y ID=id / group=id fill;
xaxis display=none offsetmax=0 offsetmin=0 ;
yaxis display=none offsetmax=0 offsetmin=0 ;
run;
``````

Rhodochrosite | Level 12

## Re: Patterns of polygons: Use SAS to create mathematical art

One of my favorites uses arcs and splines.

Barite | Level 11

## Re: Patterns of polygons: Use SAS to create mathematical art

Hi Rick,

Many thanks for the inspiring blog post. Below I am attempting to combine your 2nd and 3rd suggestions from the end of the post.  So I have modified your code to look for a random 'artwork' with no adjacent polygons of the same colour, that also uses all 16 of the pinwheels.

``````/* Rick's Code */
data Shape;
input ID x y @@;
datalines;
0  0 0  0  0.667  0.333  0  1  1  0  0  1  0  0  0
1  0 0  1 -0.333  0.667  1 -1  1  1 -1  0  1  0  0
2  0 0  2 -0.667 -0.333  2 -1 -1  2  0 -1  2  0  0
3  0 0  3  0.333 -0.667  3  1 -1  3  1  0  3  0  0
;

proc iml;
/* actions of the D4 dihedral group */
start D4Action(v, act);
/* the subroup of rotations by 90 degrees */
if      act=0 then M = { 1  0,  0  1};
else if act=1 then M = { 0 -1,  1  0};
else if act=2 then M = {-1  0,  0 -1};
else if act=3 then M = { 0  1, -1  0};
/* the subgroup of reflections across horz, vert, or diagonals */
else if act=4 then M = {-1  0,  0  1};
else if act=6 then M = { 0 -1, -1  0};
else if act=7 then M = { 1  0,  0 -1};
else if act=5 then M = { 0  1,  1  0};
return( v*M` );  /* = (M*z`)` */
finish;
/* read in the pinwheel shape */
use Shape; read all var {x y} into P1;

/* Modifying Rick's code below to output all 16 pinwheels.
Define ID2 where the odd colour numbers are switched */
ID2 = ID;
do i = 1 to 20;
if mod(ID2[i], 2)=1 then ID2[i] = 4 - ID2[i];
end;

/* write out the transformation of the pinwheel under the D4 actions */
OpNames = {"I" "R1" "R2" "R3" "S0" "S1" "S2" "S3",
"ZI" "ZR1" "ZR2" "ZR3" "ZS0" "ZS1" "ZS2" "ZS3"};
Name = OpNames[1];
Q = {. . .};
create Panel from Name Q[c={'Name' 'ID' 'x' 'y'}];
do j = 1 to 2;
do i = 0 to 7;
R = D4Action(P1, i);
if j=1 then Q = ID || R; else Q=ID2 || R;
Name = j(nrow(Q), 1, OpNames[j,i+1]);
append from Name Q;
end;
end;
close;
QUIT;

/* get colour by quadrant for each of the 16 pinwheels
Q4|Q1
-----
Q3|Q2
*/
set Panel(where=( (abs(x) + abs(y)) = 2 ));
if x=1
drop x y;
run;
proc sort; by Name quad; run;
var ID;
by Name;
run;

proc iml;
start grid_eval(x) global(c_LR, c_TB);
/* return the number of adjacent same coloured polygons in a grid of pinwheels */
nr = nrow(x);
nc = ncol(x);
count = 0;
if nc>1 then do i = 1 to nr; do j = 1 to nc-1;
count = count + c_LR[ x[i,j], x[i,j+1] ];
end; end;
if nr>1 then do i = 1 to nr-1; do j = 1 to nc;
count = count + c_TB[ x[i,j], x[i+1,j] ];
end; end;
return(count);
finish;

read all var _num_ into q;
use Panel;
read all var {ID x y} into pdata;

/* create two matrices that count the number of same coloured polygons next to each other
when 2 pinwheels are either side by side (row indexes left pinwheel, col indexes right
pinwheel) or when stacked on top of each other (row indexes top pinwheel, col indexes
bottom pinwheel) */
c_LR = j(16, 16, .);
c_TB = j(16, 16, .);
do i = 1 to 16; do j = 1 to 16;
c_LR[i, j] = sum( q[i,{1 2}] = q[j,{4 3}]);
c_TB[i, j] = sum( q[i,{3 2}] = q[j,{4 1}]);
end; end;

/* start with a random assignment of 16 pinwheels to a 4x4 grid */
g = shape(ranperm(16), 4, 4);
cc = grid_eval( g );

/* consider swaps of one pinwheel for another that might improve the artwork. Use
matrix b to keep a list for each cycle of swaps that give the best improvement */
b = j(100, 2);

do cycle = 1 to 200 until(cc=0);
bestcc = 1E20;  /* best cc this cycle */
do i = 1 to 15; do j = i+1 to 16;
h = g;
h[i] = g[j];
h[j] = g[i];
newcc = grid_eval( h );
if newcc<=bestcc then do;
if newcc<bestcc then do;
bestcc=newcc;
nb = 0;
end;
nb = nb + 1;
b[nb, 1] = i;
b[nb, 2] = j;
end;
end; end;
/* choose a swap at random from those in b */
swap = 1 + floor(rand('uniform')#nb);
besti = b[swap,1];
bestj = b[swap,2];
t = g[besti];
g[besti] = g[bestj];
g[bestj] = t;
cc = bestcc;
end;

reset noname;
if cc=0 then print 'Found grid with no adjacent same colour polygons after' cycle [format=3.0] 'swaps';
print g [format=2.0];
Q = {. . . .};
create OptPanel from Q[c={'Cell' 'ID' 'x' 'y'}];
do i = 1 to 16;
/* work out which rows of panel data correspond to ith pinwheel in g */
ridx = ( 20#(g[i]-1) + 1) : (20#g[i]);
Q = j(20,1,i) || pdata[ridx, ];
append from Q;
end;
quit;

/* Show the artwork */
%let teal   = CX288c95;
%let orange = CXeba411;
%let blue   = CX0f5098;
%let salmon = CXd5856e;
%let gray   = CX929386;

ods graphics / width=500px height=500px;
/* for convenience, define macros for the COLAXIS and ROWAXIS options */
%macro colOpts; colaxis offsetmin=0 offsetmax=0 display=(nolabel noticks novalues); %mend;
%macro rowOpts;  rowaxis offsetmin=0 offsetmax=0 display=(nolabel noticks novalues); %mend;

proc sgpanel data=OptPanel noautolegend;
styleattrs wallcolor=&gray datacolors=(&teal &orange &blue &salmon);
panelby Cell / columns=4 onepanel noheader noborder;
polygon x=x y=y ID=ID / group=ID fill;
%colOpts; %rowOpts;
run;``````

Barite | Level 11

## Re: Patterns of polygons: Use SAS to create mathematical art

It is possible to make a slightly more interesting pattern by modifying my code above as follows.  Replace the grid_eval module with this:

``````  start grid_eval(x) global(c_LR, c_TB, p_pen);
/* return the number of adjacent same coloured polygons in a grid of pinwheels */
nr = nrow(x);
nc = ncol(x);
count = 0;
if nc>1 then do i = 1 to nr; do j = 1 to nc-1;
count = count + c_LR[ x[i,j], x[i,j+1] ] + p_pen[ x[i,j], x[i,j+1] ];
end; end;
if nr>1 then do i = 1 to nr-1; do j = 1 to nc;
count = count + c_TB[ x[i,j], x[i+1,j] ] + p_pen[ x[i,j], x[i+1,j] ];
end; end;
return(count);
finish;``````

where p_pen is defined in the main program as follows:

``  p_pen = j(2, 2) @ {1 0, 0 1} @ j(4, 4);``

This modifies the criterion to penalize the number of times polygons meet at a point along the edge where two pinwheels meet.  The algorithm does not always converge to the optimum, but when it does it finds a pattern where there is a 3x3 sub-matrix of hourglass shapes visible.  For me this pattern seems to dominate, so much so that it is difficult to focus on the original 16 pinwheels.

SAS Super FREQ

## Re: Patterns of polygons: Use SAS to create mathematical art

Barite | Level 11

## Re: Patterns of polygons: Use SAS to create mathematical art

I was not really thinking about taking artistic credit for the two images above 🙂,  I shall rename the first image "Mangled Shadow" and take your suggestion of "Hourglass Shadow" for the second.

Barite | Level 11

## Re: Patterns of polygons: Use SAS to create mathematical art

I have worked-up my earlier sketch above into the final "Hourglass Shadow" 😜.  The algorithm I am using can just about cope with an 8x8 grid using your 16 pinwheels exactly four times each.

Diamond | Level 26

## Re: Patterns of polygons: Use SAS to create mathematical art

``````ods _all_ close;
ods listing;
data polygon;
file print;
put '/\' / '\/';
run;``````

It's a style of art that is called "minimalism".

Do I win?

--
Paige Miller
Pyrite | Level 9

## Re: Patterns of polygons: Use SAS to create mathematical art

``````data Pinwheels;
input Group ID @;
Group = rand("integer", 0, 15);
ID = rand("integer", 0, 3);
do i = 1 to 5;
input x y @@;
output;
end;
drop i;
datalines;
.......``````

I decided to introduce randomness by changing the variables GROUP and ID using the RAND function.

Barite | Level 11

## Re: Patterns of polygons: Use SAS to create mathematical art

Interesting to see some of the dart shapes colored in.  Presumably where one polygon has overprinted another?

Pyrite | Level 9

## Re: Patterns of polygons: Use SAS to create mathematical art

``````/* Uisng a random number to generate the pinwheels dataset */
/* Depends when the program is run, the shape can be quite different */

data pinwheels;
rand1 = rand('uniform');

group = 0; id = 0; x = 0; y = 0; output;
x = rand1; y = 1 - x; output;
x = 1; y = 1; output;
x = 0; y = 1; output;
x = 0; y = 0; output;
group = 0; id = 1; x = 0; y = 0; output;
x = rand1 - 1; y = rand1; output;
x = -1; y = 1; output;
x = -1; y = 0; output;
x = 0; y = 0; output;
group = 0; id = 2; x = 0; y = 0; output;
x = -1 * rand1; y = -1* (1 - rand1); output;
x = -1; y = -1; output;
x = 0 ; y = -1; output;
x = 0; y = 0; output;
group = 0; id = 3; x = 0; y = 0; output;
x = 1 - rand1; y = -1 * rand1; output;
x = 1; y = -1; output;
x = 1 ; y = 0; output;
x = 0; y = 0; output;
group = 1; id = 0; x = 0; y = 0; output;
x = -1*(1 - rand1); y = rand1; output;
x = -1; y = 1; output;
x = -1 ; y = 0; output;
x = 0; y = 0; output;
group = 1; id = 1; x = 0; y = 0; output;
x = -1* rand1; y = -1 *(1-rand1); output;
x = -1; y = -1; output;
x = 0 ; y = -1; output;
x = 0; y = 0; output;
group = 1; id = 2; x = 0; y = 0; output;
x = 1 - rand1; y = -1 *rand1; output;
x = 1; y = -1; output;
x = 1 ; y = 0; output;
x = 0; y = 0; output;
group = 1; id = 3; x = 0; y = 0; output;
x = rand1; y = 1 - rand1; output;
x = 1; y = 1; output;
x = 0 ; y = 1; output;
x = 0; y = 0; output;
group = 2; id = 0; x = 0; y = 0; output;
x = -1*rand1; y = -1 *(1- rand1); output;
x = -1; y = -1; output;
x = 0  ; y = -1; output;
x = 0; y = 0; output;
group = 2; id = 1; x = 0; y = 0; output;
x =  1 -rand1; y = -1 *rand1; output;
x = 1; y = -1; output;
x = 1 ; y = 0; output;
x = 0; y = 0; output;
group = 2; id = 2; x = 0; y = 0; output;
x = rand1; y = 1 - rand1; output;
x = 1; y = 1; output;
x = 0 ; y = 1; output;
x = 0; y = 0; output;
group = 2; id = 3; x = 0; y = 0; output;
x = -1 *(1- rand1); y = rand1; output;
x = -1; y = 1; output;
x = -1 ; y = 0; output;
x = 0; y = 0; output;
group = 3; id = 0; x = 0; y = 0; output;
x = 1 -rand1; y = -1 * rand1; output;
x = 1; y = -1; output;
x = 1  ; y = 0; output;
x = 0; y = 0; output;
group = 3; id = 1; x = 0; y = 0; output;
x =  rand1; y = 1 - rand1; output;
x = 1; y = 1; output;
x = 0 ; y = 1; output;
x = 0; y = 0; output;
group = 3; id = 2; x = 0; y = 0; output;
x = -1 * (1 - rand1); y = rand1; output;
x = -1; y = 1; output;
x = -1 ; y = 0; output;
x = 0; y = 0; output;
group = 3; id = 3; x = 0; y = 0; output;
x = -1 * rand1; y = -1 *(1-rand1); output;
x = -1; y = -1; output;
x = 0 ; y = -1; output;
x = 0; y = 0; output;
group = 4; id = 0; x = 0; y = 0; output;
x = 1 *rand1; y =  1 - rand1; output;
x = -1; y = 1; output;
x = 0  ; y = 1; output;
x = 0; y = 0; output;
group = 4; id = 1; x = 0; y = 0; output;
x = 1 - rand1; y = rand1; output;
x = 1; y = 1; output;
x = 1 ; y = 0; output;
x = 0; y = 0; output;
group = 4; id = 2; x = 0; y = 0; output;
x = rand1; y = -1 * (1-rand1); output;
x = 1; y = -1; output;
x = 0  ; y = -1; output;
x = 0; y = 0; output;
group = 4; id = 3; x = 0; y = 0; output;
x = -1 * (1-rand1); y = -1 * rand1; output;
x = -1; y = -1; output;
x = -1; y = 0; output;
x = 0; y = 0; output;
group = 5; id = 0; x = 0; y = 0; output;
x = 1 -rand1; y =  rand1; output;
x = 1; y = 1; output;
x = 1  ; y = 0; output;
x = 0; y = 0; output;
group = 5; id = 1; x = 0; y = 0; output;
x =  rand1; y = -1 * (1-rand1); output;
x = 1; y = -1; output;
x = 0 ; y = -1; output;
x = 0; y = 0; output;
group = 5; id = 2; x = 0; y = 0; output;
x = -1*(1-rand1); y = -1* rand1; output;
x = -1; y = -1; output;
x = -1 ; y = 0; output;
x = 0; y = 0; output;
group = 5; id = 3; x = 0; y = 0; output;
x = -1*rand1; y = 1 - rand1; output;
x = -1; y = 0; output;
x = 0; y = 1; output;
x = 0; y = 0; output;
group = 6; id = 0; x = 0; y = 0; output;
x = -1*(1 -rand1); y = -1* rand1; output;
x = -1; y = -1; output;
x = -1  ; y = 0; output;
x = 0; y = 0; output;
group = 6; id = 1; x = 0; y = 0; output;
x = -1 * rand1; y = 1-rand1; output;
x = -1; y = 1; output;
x = 0 ; y = 1; output;
x = 0; y = 0; output;
group = 6; id = 2; x = 0; y = 0; output;
x = 1-rand1; y = rand1; output;
x = 1; y = 1; output;
x = 1 ; y = 0; output;
x = 0; y = 0; output;
group = 6; id = 3; x = 0; y = 0; output;
x =  rand1; y = -1 *(1 - rand1); output;
x = 1; y = -1; output;
x = 0; y = -1; output;
x = 0; y = 0; output;
group = 7; id = 0; x = 0; y = 0; output;
x = rand1; y = -1* (1-rand1); output;
x = 1; y = -1; output;
x = 0  ; y = -1; output;
x = 0; y = 0; output;
group = 7; id = 1; x = 0; y = 0; output;
x = -1 * (1-rand1); y = -1*rand1; output;
x = -1; y = -1; output;
x = -1 ; y = 0; output;
x = 0; y = 0; output;
group = 7; id = 2; x = 0; y = 0; output;
x = -1*rand1; y = 1-rand1; output;
x = -1; y = 1; output;
x = 0 ; y = 1; output;
x = 0; y = 0; output;
group = 7; id = 3; x = 0; y = 0; output;
x = 1- rand1; y = rand1; output;
x = 1; y = 1; output;
x = 1; y = 0; output;
x = 0; y = 0; output;
group = 8; id = 0; x = 0; y = 0; output;
x = rand1; y = 1-rand1 ; output;
x = 1; y = 1; output;
x = 0  ; y = 1; output;
x = 0; y = 0; output;
group = 8; id = 1; x = 0; y = 0; output;
x = 1-rand1; y = -1* rand1; output;
x = 1; y = -1; output;
x = 1 ; y = 0; output;
x = 0; y = 0; output;
group = 8; id = 2; x = 0; y = 0; output;
x = -1*rand1; y = -1*(1-rand1); output;
x = -1; y = -1; output;
x = 0 ; y = -1; output;
x = 0; y = 0; output;
group = 8; id = 3; x = 0; y = 0; output;
x = -1*(1- rand1); y = rand1; output;
x = -1; y = 1; output;
x = -1; y = 0; output;
x = 0; y = 0; output;
group = 9; id = 0; x = 0; y = 0; output;
x = -1 * (1-rand1); y = rand1 ; output;
x = -1; y = 1; output;
x = -1  ; y = 0; output;
x = 0; y = 0; output;
group = 9; id = 1; x = 0; y = 0; output;
x = rand1; y =  1- rand1; output;
x = 1; y = 1; output;
x = 0 ; y = 1; output;
x = 0; y = 0; output;
group = 9; id = 2; x = 0; y = 0; output;
x =  1-rand1; y = -1*rand1; output;
x = 1; y = -1; output;
x = 1 ; y = 0; output;
x = 0; y = 0; output;
group = 9; id = 3; x = 0; y = 0; output;
x = -1 * rand1; y = 1-rand1; output;
x = -1; y = -1; output;
x = 0; y = -1; output;
x = 0; y = 0; output;
group =10; id = 0; x = 0; y = 0; output;
x = -1 * rand1; y = -1*(1-rand1); output;
x = -1; y = -1; output;
x = 0  ; y = 1; output;
x = 0; y = 0; output;
group =10; id = 1; x = 0; y = 0; output;
x = -1*(1-rand1); y =  rand1; output;
x = -1; y = 1; output;
x = -1 ; y = 0; output;
x = 0; y = 0; output;
group =10; id = 2; x = 0; y = 0; output;
x =  rand1; y = 1 - rand1; output;
x = 1; y = 1; output;
x = 0 ; y = 1; output;
x = 0; y = 0; output;
group =10; id = 3; x = 0; y = 0; output;
x =  1 - rand1; y = -1*rand1; output;
x = 1; y = -1; output;
x = 1; y = 0; output;
x = 0; y = 0; output;
group =11; id = 0; x = 0; y = 0; output;
x = 1 - rand1; y = -1* rand1; output;
x = 1; y = -1; output;
x = 1  ; y = 0; output;
x = 0; y = 0; output;
group =11; id = 1; x = 0; y = 0; output;
x = -1 *rand1; y = -1*(1- rand1); output;
x = -1; y = -1; output;
x = 0  ; y = -1; output;
x = 0; y = 0; output;
group =11; id = 2; x = 0; y = 0; output;
x =  -1 * (1-rand1); y = rand1; output;
x = -1; y = 1; output;
x = -1 ; y = 0; output;
x = 0; y = 0; output;
group =11; id = 3; x = 0; y = 0; output;
x =  rand1; y = 1- rand1; output;
x = 1; y = 1; output;
x = 0; y = 1; output;
x = 0; y = 0; output;
group =12; id = 0; x = 0; y = 0; output;
x = -1 *rand1; y = 1 - rand1; output;
x = -1; y = 1; output;
x = 0  ; y = 1; output;
x = 0; y = 0; output;
group =12; id = 1; x = 0; y = 0; output;
x = -1 *(1-rand1); y = -1* rand1; output;
x = -1; y = -1; output;
x = -1  ; y = 0; output;
x = 0; y = 0; output;
group =12; id = 2; x = 0; y = 0; output;
x =  rand1; y = -1 * (1-rand1); output;
x = 1 ; y = -1; output;
x = 0 ; y = -1; output;
x = 0; y = 0; output;
group =12; id = 3; x = 0; y = 0; output;
x = 1 - rand1; y = rand1; output;
x = 1; y = 1; output;
x = 1; y = 0; output;
x = 0; y = 0; output;
group =13; id = 0; x = 0; y = 0; output;
x = 1 - rand1; y = rand1; output;
x =  1; y = 1; output;
x = 1  ; y = 0; output;
x = 0; y = 0; output;
group =13; id = 1; x = 0; y = 0; output;
x = -1*rand1; y = 1 - rand1; output;
x = -1; y = 1; output;
x =  0  ; y = 1; output;
x = 0; y = 0; output;
group =13; id = 2; x = 0; y = 0; output;
x = -1 *(1- rand1); y = -1 * rand1; output;
x = -1 ; y = -1; output;
x = -1 ; y = 0; output;
x = 0; y = 0; output;
group =13; id = 3; x = 0; y = 0; output;
x = rand1; y = -1 * (1-rand1); output;
x = 1; y = -1; output;
x = 0; y = -1; output;
x = 0; y = 0; output;
group =14; id = 0; x = 0; y = 0; output;
x = -1 *(1 - rand1); y =-1 * rand1; output;
x =  -1; y = -1; output;
x = -1  ; y = 0; output;
x = 0; y = 0; output;
group =14; id = 1; x = 0; y = 0; output;
x = rand1; y = -1*(1 - rand1); output;
x = 1; y = -1; output;
x =  0  ; y = 1; output;
x = 0; y = 0; output;
group =14; id = 2; x = 0; y = 0; output;
x = 1- rand1; y = rand1; output;
x = 1 ; y = 1; output;
x = 1 ; y = 0; output;
x = 0; y = 0; output;
group =14; id = 3; x = 0; y = 0; output;
x = -1 * rand1; y = 1-rand1; output;
x = -1; y = 0; output;
x = 0; y = 1; output;
x = 0; y = 0; output;
group =15; id = 0; x = 0; y = 0; output;
x = rand1; y =-1 * (1-rand1); output;
x =  1; y = -1; output;
x = 0  ; y = -1; output;
x = 0; y = 0; output;
group =15; id = 1; x = 0; y = 0; output;
x = 1-rand1; y = rand1; output;
x = 1; y = 1; output;
x =  1  ; y = 0; output;
x = 0; y = 0; output;
group =15; id = 2; x = 0; y = 0; output;
x = -1 * rand1; y = 1-rand1; output;
x = -1 ; y = 1; output;
x = 0 ; y = 1; output;
x = 0; y = 0; output;
group =15; id = 3; x = 0; y = 0; output;
x = -1 * (1-rand1); y = -1*rand1; output;
x = -1; y = -1; output;
x = -1; y = 0; output;
x = 0; y = 0; output;
run;

%let teal   = CX288c95;
%let orange = CXeba411;
%let blue   = CX0f5098;
%let salmon = CXd5856e;
%let gray   = CX929386;

ods graphics / width=640px height=640px;
%macro colOpts; colaxis offsetmin=0 offsetmax=0 display=(nolabel noticks novalues); %mend;
%macro rowOpts;  rowaxis offsetmin=0 offsetmax=0 display=(nolabel noticks novalues); %mend;

proc sgpanel data=Pinwheels noautolegend;
styleattrs wallcolor=&gray datacolors=(&teal &orange &blue &salmon);
panelby Group / columns=4 onepanel noheader;
polygon x=x y=y ID=ID / group=ID fill;
%colOpts; %rowOpts;
run;``````

Discussion stats
• 13 replies
• 2566 views
• 35 likes
• 7 in conversation