<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:taxo="http://purl.org/rss/1.0/modules/taxonomy/" version="2.0">
  <channel>
    <title>topic Fun with SG turtles in DS2. in Graphics Programming</title>
    <link>https://communities.sas.com/t5/Graphics-Programming/Fun-with-SG-turtles-in-DS2/m-p/647292#M19880</link>
    <description>&lt;P&gt;Find attached a Proc DS2 package and sample usage code for simple turtle graphics.&amp;nbsp; The package takes turtle instructions issued through DS2 package instances and translates them to source code invocations of macros in &lt;A href="https://documentation.sas.com/?docsetId=grstatproc&amp;amp;docsetTarget=n1bd1o01uscfusn182luguuxtht1.htm&amp;amp;docsetVersion=9.4&amp;amp;locale=en" target="_self"&gt;SG Annotation Macro Dictionary&lt;/A&gt;&amp;nbsp;which are stored in a data set.&amp;nbsp; The example code draws some images, and writes the generated source code to a file that is included back.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RichardADeVenezia_0-1589335373039.png" style="width: 400px;"&gt;&lt;img src="https://communities.sas.com/t5/image/serverpage/image-id/39356iD2A23BB5FDE6D7C8/image-size/medium?v=v2&amp;amp;px=400" role="button" title="RichardADeVenezia_0-1589335373039.png" alt="RichardADeVenezia_0-1589335373039.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RichardADeVenezia_1-1589335406289.png" style="width: 400px;"&gt;&lt;img src="https://communities.sas.com/t5/image/serverpage/image-id/39357i1D979135CEC8CA86/image-size/medium?v=v2&amp;amp;px=400" role="button" title="RichardADeVenezia_1-1589335406289.png" alt="RichardADeVenezia_1-1589335406289.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Demo code is in the Spoiler because the message board would &lt;STRONG&gt;not&lt;/STRONG&gt; accept it as an attachment.&lt;/P&gt;
&lt;LI-SPOILER&gt;
&lt;PRE&gt;%* determine folder that program is in;

%let program_path = %sysfunc(transtrn(%sysget(sas_execfilepath),%sysget(sas_execfilename),));

%include "&amp;amp;program_path.DS2SGTI - package source.sas";


%let image_viewer = C:\Users\Richard\FSViewer74\FSViewer.exe;
%let image_folder = D:\TurtleOutput;

options source;

%if not %sysfunc(cexist(work.sasmacr.sganno.macro)) %then %do;
  %sganno

  %* create template for playing the annotations;
  ods path(prepend) work.templat(update);
  proc template;
    define statgraph DS2GTI;
      begingraph;
       layout overlay;
         annotate;            %* specify the ANNOTATE GTL statement ;
       endlayout;
      endgraph;
    end;
  run;
%end;

%macro break;
  %abort cancel;
%mend;

%macro fdelete (filename);
  filename _file "&amp;amp;filename";
  %let rc = %sysfunc(fdelete(_file));
  %put NOTE: %sysfunc(sysmsg());
  filename _file;
%mend;

options ls=250;

ods html close;
ods listing;

proc ds2;
  * A data program;
  data _null_;
    * global variables;

    declare char(41) statements_table;       /* table that will collect the generated statements */
    declare varchar(400) write_options;      /* options for %sgtext statements that will be generated */
    declare varchar(300) path;               /* path where image files will be written */

    declare package math math();

    method init();
      * global assignments for use in other methods;
      statements_table = 'work.turtle_statements';
      write_options = 'anchor="left", textsize=14, textfont="Albany AMT"';

      /*
       * NOTE:
       *   DS2 has no methods for interacting with the macro environment at run-time.
       *   The closest one can get is to resolve a macro symbol at source code submission time
       */
      path = %str(%'&amp;amp;image_folder%');
    end;

    method sample1();
      declare package canvas c;
      declare package turtle t;

      c = _new_ canvas(900,900, statements_table, path, 'sample1');
      t = _new_ turtle(c);

      put statements_table=;

      t.down();
      t.advance(200);   t.arc(150,90,8);
      t.advance(200);   t.arc(50,90,8);
      t.advance(450);   t.arc(150,90,8);
      t.advance(450);   t.arc(150,90,8);
      t.advance(450);   t.arc(50,90,8);
      t.advance(200);   t.arc(50,90,8);
      t.advance(300);   t.arc(-50,180,8); t.right(90);
      t.advance(50);

      t.up();
      t.setxy (-425,410);
      t.down();
      t.write(c.filename(), write_options);

      c.close();
      c.delete();
    end;

    method sample2();
      declare package canvas c;
      declare package turtle t;

      declare int index;

      c = _new_ canvas(900,900, statements_table, path, 'sample2');
      t = _new_ turtle(c);

      t.down();
      do index = 1 to 96;
        t.advance(10 + 10 * index);
        t.left(90);
      end;

      t.up();
      t.setxy (-425,410);
      t.down();
      t.write(c.filename(), write_options);

      t.delete();
      c.close();
      c.delete();
    end;

    method sample3();
      declare package canvas c;
      declare package turtle t;

      declare int index r g b;
      declare double theta;

      c = _new_ canvas(900,900, statements_table, path, 'sample3');
      t = _new_ turtle(c);
      t.down();

      r = 50;
      g = 100;
      b = 150;

      do index = 1 to 350;
        r = mod(r+2,256);
        g = mod(g+3,256);
        b = mod(b+4,256);

        t.color(c.rgb_to_color(r,g,b));

        t.advance(25 + index);
        t.left(70);

        theta = constant('pi') + 8 * constant('pi') * index / 150;

        t.size(8 + 7 * cos(theta));
      end;

      t.up();
      t.setxy (-425,410);
      t.down();
      t.write(c.filename(), write_options);

      t.delete();
      c.close();
      c.delete();
    end;

    method chord_image_for_ratio(double radius, double p, double q);
      declare double angle chord_angle chord_length outside dstep;

      declare package canvas c;
      declare package turtle t;

      c = _new_ canvas(900,900, statements_table, 'chords_image_for_ratio_anno', path, 'chords image ratio '||put(p,4.)||' '||put(q,4.));
      t = _new_ turtle(c);

      angle = 360. / p;
      chord_angle = q * angle;
      chord_length = 2. * radius * sin(math.radians(chord_angle / 2));

      outside = 360. * q / p;

      t.up();
      t.setxy(
        radius * cos(math.radians(180 + (180-chord_angle)/2 )),
        radius * sin(math.radians(180 + (180-chord_angle)/2 ))
      );
      t.down();

      do dstep = 1 to p;
        t.advance(chord_length);
        t.left(outside);
      end;

      t.up(); t.setxy (-425,410); t.down(); t.write(c.filename(), write_options);
      t.up(); t.setxy (-425,375); t.down(); t.write(p||':'||q, write_options);
      t.up(); t.setxy (-425,340); t.down(); t.write(strip(put(angle,best7.)) || ' ' || strip(put(outside,best7.)), write_options);

      t.delete();
      c.close();
      c.delete();
    end;

    method chord_images_for_ratios(int minp, int maxp);
      declare double radius dstep;
      declare int p1 q1 p q;
      declare char(1) flag1 flag2;

      radius = 415;

      do p1 = minp to maxp;
        do q1 = 1 to maxp/2;
          math.rational ( 1.* p1 / q1, 1000, p, q );

          flag1 = ifc (p &amp;lt; p1, '*', ' ');
          flag2 = ifc (p &amp;lt; 2*q,'#', ' ');

          * put p1=3. q1=3. p=3. q=3. ' ' flag1 ' ' flag2;

          if p &amp;lt; p1 then continue;
          if p &amp;lt; 2*q then continue;

          chord_image_for_ratio(radius, p, q);
        end;
      end;
    end;

    method chord_image_for_angle(double radius, double theta);
      declare double outside nangles angle chord_angle chord_length dstep;
      declare int p q steps;

      declare package canvas c;
      declare package turtle t;

      outside = 180. - theta;
      nangles = 360. / outside;

      math.rational ( nangles, 1000, p, q );

      steps = p;

      angle = 360. / p;

      c = _new_ canvas(900,900, statements_table, 'chords_image_for_angle_anno', path, 'chords image angle '||put(theta,6.2));
      t = _new_ turtle(c);

/*    * draw outline;*/
/*    t.setxy(0,-radius); t.down(); t.arc(radius,360,180); t.up();*/

      chord_angle = q * angle;

      chord_length = 2 * radius * sin(math.radians(chord_angle / 2));

      t.up();
      t.setxy(
        radius * cos(math.radians(180 + (180-chord_angle)/2 )),
        radius * sin(math.radians(180 + (180-chord_angle)/2 ))
      );
      t.down();

      do dstep = 1 to steps;
        t.advance(chord_length);
        t.left(outside);
      end;

      t.up(); t.setxy (-425,410); t.down(); t.write(c.filename(), write_options);
      t.up(); t.setxy (-425,375); t.down(); t.write(p||' '||q, write_options);
      t.up(); t.setxy (-425,340); t.down(); t.write(angle, write_options);
      t.up(); t.setxy (-425,305); t.down(); t.write(theta, write_options);

      c.close();
      c.delete();
    end;

    method chord_images_for_angles(double mintheta, double maxtheta, double delta);
      declare double radius theta;

      radius = 415;
      do theta = mintheta to maxtheta by delta;
        chord_image_for_angle (radius, theta);
      end;
    end;

    method swirly_triangle();
      declare package canvas c;
      declare package turtle t;

      declare int index;

      c = _new_ canvas(900,900, statements_table, path, 'swirly_triangle');
      t = _new_ turtle(c);

      t.down();
      do index = 1 to 150;
        t.advance(8 + 6 * index);
        t.left(121);
      end;

      t.up();
      t.setxy (-425,410);
      t.down();
      t.write(c.filename(), write_options);

      t.delete();
      c.close();
      c.delete();
    end;

    method run();
      * rebuild statements table;
      sqlexec ('drop   table ' || statements_table || ' force');
      sqlexec ('create table ' || statements_table || ' (statement char(400))');

/*      sample1();*/
/*      sample2();*/
      sample3();

/*      chord_images_for_angles(7, 10, 1);*/
/*      chord_images_for_ratios(5, 23);*/

      swirly_triangle();
    end;

  enddata;
run;
quit;

%let syslast = work.turtle_statements;

options source xmin noxwait noxsync;
options nosource2;

filename stmt_pgm temp;
data _null_;
  set &amp;amp;syslast end=last;
  file stmt_pgm;
  put statement;
  if last then call execute ('%nrstr(%include stmt_pgm;)');
run;

filename stmt_pgm;

options noxwait noxmin noxsync;

/* %sysexec "&amp;amp;image_viewer" "&amp;amp;image_folder.\swirly_triangle.png"; */
&lt;/PRE&gt;
&lt;/LI-SPOILER&gt;
&lt;P&gt;Packages and methods:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Math
&lt;UL&gt;
&lt;LI&gt;radians()&lt;/LI&gt;
&lt;LI&gt;rational()&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;Pen
&lt;UL&gt;
&lt;LI&gt;up()&lt;/LI&gt;
&lt;LI&gt;down()&lt;/LI&gt;
&lt;LI&gt;isDown()&lt;/LI&gt;
&lt;LI&gt;isUp()&lt;/LI&gt;
&lt;LI&gt;color()&lt;/LI&gt;
&lt;LI&gt;size()&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;Canvas
&lt;UL&gt;
&lt;LI&gt;translate()&lt;/LI&gt;
&lt;LI&gt;rgb_to_color()&lt;/LI&gt;
&lt;LI&gt;setpen()&lt;/LI&gt;
&lt;LI&gt;line()&lt;/LI&gt;
&lt;LI&gt;arc()&lt;/LI&gt;
&lt;LI&gt;filename()&lt;/LI&gt;
&lt;LI&gt;write()&amp;nbsp; /* text */&lt;/LI&gt;
&lt;LI&gt;close()&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;Turtle
&lt;UL&gt;
&lt;LI&gt;log()&lt;/LI&gt;
&lt;LI&gt;advance()&amp;nbsp;&lt;/LI&gt;
&lt;LI&gt;retreat()&lt;/LI&gt;
&lt;LI&gt;right()&lt;/LI&gt;
&lt;LI&gt;left()&lt;/LI&gt;
&lt;LI&gt;setxy()&lt;/LI&gt;
&lt;LI&gt;setx()&lt;/LI&gt;
&lt;LI&gt;sety()&lt;/LI&gt;
&lt;LI&gt;getx()&lt;/LI&gt;
&lt;LI&gt;gety()&lt;/LI&gt;
&lt;LI&gt;heading()&lt;/LI&gt;
&lt;LI&gt;home()&lt;/LI&gt;
&lt;LI&gt;arc()&lt;/LI&gt;
&lt;LI&gt;dot()&lt;/LI&gt;
&lt;LI&gt;down()&lt;/LI&gt;
&lt;LI&gt;up()&lt;/LI&gt;
&lt;LI&gt;color()&lt;/LI&gt;
&lt;LI&gt;size()&lt;/LI&gt;
&lt;LI&gt;write()&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The sources are named DS2SGTI for "DS2 Statistical Graphics Turtle Interface".&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Back story:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;The olden days of SAS/GRAPH provided the DATA Step Graphics Interface (DSGI),&amp;nbsp; a variety of functions that mimicked an implementation of the ISO Graphic Kernel Standard (GKS).&amp;nbsp; &amp;nbsp;DSGI documentation went dark in SAS 9.4, and DSGI I presume is in a deprecated state.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;At one point I had written Proc FCMP function adapters to the DSGI functions, in the hopes they could be called from Proc DS2.&amp;nbsp; Turns out the DSGI &lt;EM&gt;.dll&lt;/EM&gt; that holds all those functions is not accessible to DS2 code, even when called through a FCMP function adapter.&amp;nbsp; I presume this is related to how DS2 programs can run in CAS which wouldn't have the DSGI functions available.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;SAS/GRAPH Graph Template Language (GTL) is the new game in town, but there is no equivalent functional interface like DSGI was.&amp;nbsp; I suppose if there were one it would be called SGDSGI or DSGTLI.&lt;/P&gt;</description>
    <pubDate>Wed, 13 May 2020 02:26:23 GMT</pubDate>
    <dc:creator>RichardDeVen</dc:creator>
    <dc:date>2020-05-13T02:26:23Z</dc:date>
    <item>
      <title>Fun with SG turtles in DS2.</title>
      <link>https://communities.sas.com/t5/Graphics-Programming/Fun-with-SG-turtles-in-DS2/m-p/647292#M19880</link>
      <description>&lt;P&gt;Find attached a Proc DS2 package and sample usage code for simple turtle graphics.&amp;nbsp; The package takes turtle instructions issued through DS2 package instances and translates them to source code invocations of macros in &lt;A href="https://documentation.sas.com/?docsetId=grstatproc&amp;amp;docsetTarget=n1bd1o01uscfusn182luguuxtht1.htm&amp;amp;docsetVersion=9.4&amp;amp;locale=en" target="_self"&gt;SG Annotation Macro Dictionary&lt;/A&gt;&amp;nbsp;which are stored in a data set.&amp;nbsp; The example code draws some images, and writes the generated source code to a file that is included back.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RichardADeVenezia_0-1589335373039.png" style="width: 400px;"&gt;&lt;img src="https://communities.sas.com/t5/image/serverpage/image-id/39356iD2A23BB5FDE6D7C8/image-size/medium?v=v2&amp;amp;px=400" role="button" title="RichardADeVenezia_0-1589335373039.png" alt="RichardADeVenezia_0-1589335373039.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&lt;span class="lia-inline-image-display-wrapper lia-image-align-inline" image-alt="RichardADeVenezia_1-1589335406289.png" style="width: 400px;"&gt;&lt;img src="https://communities.sas.com/t5/image/serverpage/image-id/39357i1D979135CEC8CA86/image-size/medium?v=v2&amp;amp;px=400" role="button" title="RichardADeVenezia_1-1589335406289.png" alt="RichardADeVenezia_1-1589335406289.png" /&gt;&lt;/span&gt;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Demo code is in the Spoiler because the message board would &lt;STRONG&gt;not&lt;/STRONG&gt; accept it as an attachment.&lt;/P&gt;
&lt;LI-SPOILER&gt;
&lt;PRE&gt;%* determine folder that program is in;

%let program_path = %sysfunc(transtrn(%sysget(sas_execfilepath),%sysget(sas_execfilename),));

%include "&amp;amp;program_path.DS2SGTI - package source.sas";


%let image_viewer = C:\Users\Richard\FSViewer74\FSViewer.exe;
%let image_folder = D:\TurtleOutput;

options source;

%if not %sysfunc(cexist(work.sasmacr.sganno.macro)) %then %do;
  %sganno

  %* create template for playing the annotations;
  ods path(prepend) work.templat(update);
  proc template;
    define statgraph DS2GTI;
      begingraph;
       layout overlay;
         annotate;            %* specify the ANNOTATE GTL statement ;
       endlayout;
      endgraph;
    end;
  run;
%end;

%macro break;
  %abort cancel;
%mend;

%macro fdelete (filename);
  filename _file "&amp;amp;filename";
  %let rc = %sysfunc(fdelete(_file));
  %put NOTE: %sysfunc(sysmsg());
  filename _file;
%mend;

options ls=250;

ods html close;
ods listing;

proc ds2;
  * A data program;
  data _null_;
    * global variables;

    declare char(41) statements_table;       /* table that will collect the generated statements */
    declare varchar(400) write_options;      /* options for %sgtext statements that will be generated */
    declare varchar(300) path;               /* path where image files will be written */

    declare package math math();

    method init();
      * global assignments for use in other methods;
      statements_table = 'work.turtle_statements';
      write_options = 'anchor="left", textsize=14, textfont="Albany AMT"';

      /*
       * NOTE:
       *   DS2 has no methods for interacting with the macro environment at run-time.
       *   The closest one can get is to resolve a macro symbol at source code submission time
       */
      path = %str(%'&amp;amp;image_folder%');
    end;

    method sample1();
      declare package canvas c;
      declare package turtle t;

      c = _new_ canvas(900,900, statements_table, path, 'sample1');
      t = _new_ turtle(c);

      put statements_table=;

      t.down();
      t.advance(200);   t.arc(150,90,8);
      t.advance(200);   t.arc(50,90,8);
      t.advance(450);   t.arc(150,90,8);
      t.advance(450);   t.arc(150,90,8);
      t.advance(450);   t.arc(50,90,8);
      t.advance(200);   t.arc(50,90,8);
      t.advance(300);   t.arc(-50,180,8); t.right(90);
      t.advance(50);

      t.up();
      t.setxy (-425,410);
      t.down();
      t.write(c.filename(), write_options);

      c.close();
      c.delete();
    end;

    method sample2();
      declare package canvas c;
      declare package turtle t;

      declare int index;

      c = _new_ canvas(900,900, statements_table, path, 'sample2');
      t = _new_ turtle(c);

      t.down();
      do index = 1 to 96;
        t.advance(10 + 10 * index);
        t.left(90);
      end;

      t.up();
      t.setxy (-425,410);
      t.down();
      t.write(c.filename(), write_options);

      t.delete();
      c.close();
      c.delete();
    end;

    method sample3();
      declare package canvas c;
      declare package turtle t;

      declare int index r g b;
      declare double theta;

      c = _new_ canvas(900,900, statements_table, path, 'sample3');
      t = _new_ turtle(c);
      t.down();

      r = 50;
      g = 100;
      b = 150;

      do index = 1 to 350;
        r = mod(r+2,256);
        g = mod(g+3,256);
        b = mod(b+4,256);

        t.color(c.rgb_to_color(r,g,b));

        t.advance(25 + index);
        t.left(70);

        theta = constant('pi') + 8 * constant('pi') * index / 150;

        t.size(8 + 7 * cos(theta));
      end;

      t.up();
      t.setxy (-425,410);
      t.down();
      t.write(c.filename(), write_options);

      t.delete();
      c.close();
      c.delete();
    end;

    method chord_image_for_ratio(double radius, double p, double q);
      declare double angle chord_angle chord_length outside dstep;

      declare package canvas c;
      declare package turtle t;

      c = _new_ canvas(900,900, statements_table, 'chords_image_for_ratio_anno', path, 'chords image ratio '||put(p,4.)||' '||put(q,4.));
      t = _new_ turtle(c);

      angle = 360. / p;
      chord_angle = q * angle;
      chord_length = 2. * radius * sin(math.radians(chord_angle / 2));

      outside = 360. * q / p;

      t.up();
      t.setxy(
        radius * cos(math.radians(180 + (180-chord_angle)/2 )),
        radius * sin(math.radians(180 + (180-chord_angle)/2 ))
      );
      t.down();

      do dstep = 1 to p;
        t.advance(chord_length);
        t.left(outside);
      end;

      t.up(); t.setxy (-425,410); t.down(); t.write(c.filename(), write_options);
      t.up(); t.setxy (-425,375); t.down(); t.write(p||':'||q, write_options);
      t.up(); t.setxy (-425,340); t.down(); t.write(strip(put(angle,best7.)) || ' ' || strip(put(outside,best7.)), write_options);

      t.delete();
      c.close();
      c.delete();
    end;

    method chord_images_for_ratios(int minp, int maxp);
      declare double radius dstep;
      declare int p1 q1 p q;
      declare char(1) flag1 flag2;

      radius = 415;

      do p1 = minp to maxp;
        do q1 = 1 to maxp/2;
          math.rational ( 1.* p1 / q1, 1000, p, q );

          flag1 = ifc (p &amp;lt; p1, '*', ' ');
          flag2 = ifc (p &amp;lt; 2*q,'#', ' ');

          * put p1=3. q1=3. p=3. q=3. ' ' flag1 ' ' flag2;

          if p &amp;lt; p1 then continue;
          if p &amp;lt; 2*q then continue;

          chord_image_for_ratio(radius, p, q);
        end;
      end;
    end;

    method chord_image_for_angle(double radius, double theta);
      declare double outside nangles angle chord_angle chord_length dstep;
      declare int p q steps;

      declare package canvas c;
      declare package turtle t;

      outside = 180. - theta;
      nangles = 360. / outside;

      math.rational ( nangles, 1000, p, q );

      steps = p;

      angle = 360. / p;

      c = _new_ canvas(900,900, statements_table, 'chords_image_for_angle_anno', path, 'chords image angle '||put(theta,6.2));
      t = _new_ turtle(c);

/*    * draw outline;*/
/*    t.setxy(0,-radius); t.down(); t.arc(radius,360,180); t.up();*/

      chord_angle = q * angle;

      chord_length = 2 * radius * sin(math.radians(chord_angle / 2));

      t.up();
      t.setxy(
        radius * cos(math.radians(180 + (180-chord_angle)/2 )),
        radius * sin(math.radians(180 + (180-chord_angle)/2 ))
      );
      t.down();

      do dstep = 1 to steps;
        t.advance(chord_length);
        t.left(outside);
      end;

      t.up(); t.setxy (-425,410); t.down(); t.write(c.filename(), write_options);
      t.up(); t.setxy (-425,375); t.down(); t.write(p||' '||q, write_options);
      t.up(); t.setxy (-425,340); t.down(); t.write(angle, write_options);
      t.up(); t.setxy (-425,305); t.down(); t.write(theta, write_options);

      c.close();
      c.delete();
    end;

    method chord_images_for_angles(double mintheta, double maxtheta, double delta);
      declare double radius theta;

      radius = 415;
      do theta = mintheta to maxtheta by delta;
        chord_image_for_angle (radius, theta);
      end;
    end;

    method swirly_triangle();
      declare package canvas c;
      declare package turtle t;

      declare int index;

      c = _new_ canvas(900,900, statements_table, path, 'swirly_triangle');
      t = _new_ turtle(c);

      t.down();
      do index = 1 to 150;
        t.advance(8 + 6 * index);
        t.left(121);
      end;

      t.up();
      t.setxy (-425,410);
      t.down();
      t.write(c.filename(), write_options);

      t.delete();
      c.close();
      c.delete();
    end;

    method run();
      * rebuild statements table;
      sqlexec ('drop   table ' || statements_table || ' force');
      sqlexec ('create table ' || statements_table || ' (statement char(400))');

/*      sample1();*/
/*      sample2();*/
      sample3();

/*      chord_images_for_angles(7, 10, 1);*/
/*      chord_images_for_ratios(5, 23);*/

      swirly_triangle();
    end;

  enddata;
run;
quit;

%let syslast = work.turtle_statements;

options source xmin noxwait noxsync;
options nosource2;

filename stmt_pgm temp;
data _null_;
  set &amp;amp;syslast end=last;
  file stmt_pgm;
  put statement;
  if last then call execute ('%nrstr(%include stmt_pgm;)');
run;

filename stmt_pgm;

options noxwait noxmin noxsync;

/* %sysexec "&amp;amp;image_viewer" "&amp;amp;image_folder.\swirly_triangle.png"; */
&lt;/PRE&gt;
&lt;/LI-SPOILER&gt;
&lt;P&gt;Packages and methods:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Math
&lt;UL&gt;
&lt;LI&gt;radians()&lt;/LI&gt;
&lt;LI&gt;rational()&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;Pen
&lt;UL&gt;
&lt;LI&gt;up()&lt;/LI&gt;
&lt;LI&gt;down()&lt;/LI&gt;
&lt;LI&gt;isDown()&lt;/LI&gt;
&lt;LI&gt;isUp()&lt;/LI&gt;
&lt;LI&gt;color()&lt;/LI&gt;
&lt;LI&gt;size()&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;Canvas
&lt;UL&gt;
&lt;LI&gt;translate()&lt;/LI&gt;
&lt;LI&gt;rgb_to_color()&lt;/LI&gt;
&lt;LI&gt;setpen()&lt;/LI&gt;
&lt;LI&gt;line()&lt;/LI&gt;
&lt;LI&gt;arc()&lt;/LI&gt;
&lt;LI&gt;filename()&lt;/LI&gt;
&lt;LI&gt;write()&amp;nbsp; /* text */&lt;/LI&gt;
&lt;LI&gt;close()&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;LI&gt;Turtle
&lt;UL&gt;
&lt;LI&gt;log()&lt;/LI&gt;
&lt;LI&gt;advance()&amp;nbsp;&lt;/LI&gt;
&lt;LI&gt;retreat()&lt;/LI&gt;
&lt;LI&gt;right()&lt;/LI&gt;
&lt;LI&gt;left()&lt;/LI&gt;
&lt;LI&gt;setxy()&lt;/LI&gt;
&lt;LI&gt;setx()&lt;/LI&gt;
&lt;LI&gt;sety()&lt;/LI&gt;
&lt;LI&gt;getx()&lt;/LI&gt;
&lt;LI&gt;gety()&lt;/LI&gt;
&lt;LI&gt;heading()&lt;/LI&gt;
&lt;LI&gt;home()&lt;/LI&gt;
&lt;LI&gt;arc()&lt;/LI&gt;
&lt;LI&gt;dot()&lt;/LI&gt;
&lt;LI&gt;down()&lt;/LI&gt;
&lt;LI&gt;up()&lt;/LI&gt;
&lt;LI&gt;color()&lt;/LI&gt;
&lt;LI&gt;size()&lt;/LI&gt;
&lt;LI&gt;write()&lt;/LI&gt;
&lt;/UL&gt;
&lt;/LI&gt;
&lt;/UL&gt;
&lt;P&gt;The sources are named DS2SGTI for "DS2 Statistical Graphics Turtle Interface".&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;Back story:&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;The olden days of SAS/GRAPH provided the DATA Step Graphics Interface (DSGI),&amp;nbsp; a variety of functions that mimicked an implementation of the ISO Graphic Kernel Standard (GKS).&amp;nbsp; &amp;nbsp;DSGI documentation went dark in SAS 9.4, and DSGI I presume is in a deprecated state.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;At one point I had written Proc FCMP function adapters to the DSGI functions, in the hopes they could be called from Proc DS2.&amp;nbsp; Turns out the DSGI &lt;EM&gt;.dll&lt;/EM&gt; that holds all those functions is not accessible to DS2 code, even when called through a FCMP function adapter.&amp;nbsp; I presume this is related to how DS2 programs can run in CAS which wouldn't have the DSGI functions available.&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;SAS/GRAPH Graph Template Language (GTL) is the new game in town, but there is no equivalent functional interface like DSGI was.&amp;nbsp; I suppose if there were one it would be called SGDSGI or DSGTLI.&lt;/P&gt;</description>
      <pubDate>Wed, 13 May 2020 02:26:23 GMT</pubDate>
      <guid>https://communities.sas.com/t5/Graphics-Programming/Fun-with-SG-turtles-in-DS2/m-p/647292#M19880</guid>
      <dc:creator>RichardDeVen</dc:creator>
      <dc:date>2020-05-13T02:26:23Z</dc:date>
    </item>
  </channel>
</rss>

